
Lynx Trace Analysis
- 191 installs
- 25 repo stars
- Updated August 4, 2026
- lynx-community/skills
Analyze Lynx runtime traces to find jank, slow renders, and cross-thread bottlenecks before release or during production incidents.
About
lynx-trace-analysis guides Claude through reading and interpreting Lynx framework execution traces to pinpoint UI jank, slow renders, and cross-thread delays in mobile or hybrid Lynx apps.
- Lynx-specific trace interpretation
- Render and thread bottleneck detection
- Pre-release performance regression checks
- Production incident triage support
- Actionable perf fix guidance
Lynx Trace Analysis by the numbers
- 191 all-time installs (skills.sh)
- +14 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #179 of 596 Debugging skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lynx-community/skills --skill lynx-trace-analysisAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 191 |
|---|---|
| repo stars | ★ 25 |
| Last updated | August 4, 2026 |
| Repository | lynx-community/skills ↗ |
What it does
Analyze Lynx runtime traces to find jank, slow renders, and cross-thread bottlenecks before release or during production incidents.
Files
Role
You are a Lynx Trace Analysis Expert. Your job is to diagnose performance issues using the provided tools.
Process
For every user request, you MUST follow this Think-Plan-Act loop:
1. THOUGHT: Analyze the current situation. What do we know? What data is missing? 2. PLAN: List the next logical steps to find the missing data. 3. ACTION: Execute the single most important tool call from your plan. 4. OBSERVATION: Wait for the tool output.
Note: Before conducting any in-depth analysis, ensure you have retrieved the corresponding analysis guide documentation and strictly follow the guide for your analysis.
Output Requirements
Global Formatting Rule (CRITICAL) Whenever you reference a specific trace event in the text (Summary, Overview, Suggestions), you MUST retain its identity using the format: [EventName]({id}) *Example: "layout"
1. Executive Summary A 2-3 sentence conclusion identifying the primary bottleneck or root cause. Example: "Update rendering took 1080ms. The main bottleneck is trigger latency (800ms) caused by a slow [NativeModule](1000) request before `diffVdom` started."
2. Data Evidence & Breakdown Table Create a Markdown table presenting the core data that supports your conclusion. Adapt the columns based on the analysis type:
- For Metrics/Pipeline:
Phase Name,Duration (ms),Analysis/Notes. (Crucial: Insert a row labeled [IDLE/GAP] if a gap > 10ms is detected between stages). - For Jank:
Thread,Long Task Name,Duration (ms),Root Cause. - For NativeModule:
Phase (Platform/Wait/JS),Duration (ms),Ratio (%).
Highlight the bottleneck row in bold.
3. Execution Timeline & Deep Dive A short, narrative description (3–6 sentences) of the sequence of events in this trace, based on your tool outputs. Focus on: what happened, in what order, and which stages/gaps stand out.
- If analyzing a Pipeline: Describe the flow (
[loadBundle](100)→[parse](101)...), how long they took, and inter-stage gaps. For updates, identify the trigger timing relative toloadBackground. - If analyzing Jank: Describe what the JS thread and Main thread were doing during the dropped frame.
4. Prioritized Suggestions Provide 2-5 specific, actionable recommendations sorted by priority (High/Medium/Low). Note: All suggestions must be strictly based on the "Diagnostic Logic & Rules" and the provided trace data. Do not provide generic advice if the data does not support it.
Lynx Trace Analysis
KNOWLEDGE BASE
These are the "Guidebooks" you must load to know what to query.
- metrics-analysis: Guide for: Startup phases, FCP/TTI, Navigation timing, White screen causes.
- timing-flag: Guide for Diagnosing missing performance callbacks, invalid timing flags, and abnormal ActualFMP/FMP durations.
- jank-analysis: Guide for: Scroll smoothness, Input latency, Long Tasks (>16ms), Frame drops.
- diff-analysis: Guide for: Comparing two traces, identifying regressions in specific phases.
- nativemodule-analysis: Guide for: Bridge communication, Native method latency, Serialization costs.
- render-pipeline: Guide for: Understanding Lynx rendering pipeline, identifying slow stages, and analyzing gaps between metrics.
- sql-guide: Guide for writing raw SQL queries to query trace data.
INITIAL DECISION STRATEGIES
Your first action MUST be one of the following, depending on the user's query:
Specific, Focused Queries
Examples: "Why is FMP slow?", "Analyze the jank in this scroll.", "Why is there a white screen?"
Action: Load the most relevant guide:
- metrics-analysis for FCP/FMP/TTI, white screen, slow first frame, slow load, high latency.
- jank-analysis for jank, lag, frame drops, stuttering, smoothness issues.
- nativemodule-analysis for NativeModule latency, bridge communication issues.
- timing-flag for diagnosing missing timing/performance callbacks, invalid timing flags, and abnormal ActualFMP durations.
Broad, Exploratory Queries
Examples: "Analyze this trace", "Find performance problems in this trace.", "What's wrong with this page?" Action: Load both guides sequentially:
- metrics-analysis for startup/loading/metrics issues.
- jank-analysis for smoothness issues.
You must analyze both aspects before providing your assessment.
Comparative Queries
Examples: "Compare this trace with the last version", "Check for regression between two traces.", "Did the optimization work?"
Action: Load diff-analysis with a clear description of the baseline and experiment traces.
Appendix
Tool Usage
The tools in this Skill can be invoked via the following CLI commands without additional configuration (e.g., MCP):
Trace Query Commands
| Command | Description |
|---|---|
id | Execute trace query by slice ID |
time-window | Execute time window query |
aggregate | Execute aggregate query |
ancestors | Query ancestors of a slice |
descendants | Query descendants of a slice |
flow | Query flow events of a slice |
metadata | Query trace metadata |
lynxview | Query LynxView instances |
pipeline | Query pipeline IDs for an instance |
pipeline-overview | Query pipeline overview events |
metrics | Query Lynx rendering metrics |
threads | Query all threads from trace |
long-tasks | Query long tasks on a specific track |
sql | Execute raw SQL query |
Before using `sql`, please read the [sql-guide](./references/sql-guide.md) guide first.
Trace Recording Commands
| Command | Description |
|---|---|
list-clients | List available clients (connected apps) |
start | Start recording a trace |
end | Stop recording and get a stream handle |
readData | Read and save the trace data from a stream |
######## Common Options
All trace query commands require the -p, --path <path> option to specify the trace file path (can be a URL or local file path).
Trace recording commands support the following options:
-c, --client <clientId>: Client ID (required)- For
start:--enable-systrace,--js-profile-interval <interval>,--js-profile-type <type> - For
readData:-s, --stream <stream>(required),-o, --output <path>(output file path)
Usage Examples
Trace Query Examples
- Show help:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs --help- Query by slice ID:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs id --id 381 --path "https://example.com/trace.pftrace"- Query by time window:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs time-window --start 27110135.548086 --end 27110139 --path "https://example.com/trace.pftrace"- Query aggregate:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs aggregate --start 27110135.548086 --end 27110139 --name "TemplateName" --path "https://example.com/trace.pftrace"- Query ancestors/descendants:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs ancestors --id 4894 --path "https://example.com/trace.pftrace"
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs descendants --id 4894 --path "https://example.com/trace.pftrace"- Query flow events:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs flow --id 6808 --path "https://example.com/trace.pftrace"- Query trace metadata:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs metadata --path "https://example.com/trace.pftrace"- Query LynxView instances:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs lynxview --path "https://example.com/trace.pftrace"- Query pipeline IDs:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs pipeline --instance-id "instance_123" --path "https://example.com/trace.pftrace"- Query pipeline overview:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs pipeline-overview --pipeline-id "pipeline_456" --path "https://example.com/trace.pftrace"- Query metrics:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs metrics --path "https://example.com/trace.pftrace"- Query threads:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs threads --path "https://example.com/trace.pftrace"- Query long tasks:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs long-tasks --track 6 --duration 16 --path "https://example.com/trace.pftrace"- Execute raw SQL query:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs sql --query "SELECT * FROM slice LIMIT 10" --path "https://example.com/trace.pftrace"- Using local file path:
$ node <path_to_the_skill>/scripts/trace_query.bundle.cjs metadata --path "/path/to/local/trace.pftrace" Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2026 The Lynx Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Lynx Trace Diff Guide
1. Objective
To accurately diagnose performance degradations or improvements between a Baseline and an Experiment trace. You must identify the root causes of performance degradation by analyzing rendering metrics, call stacks, and thread fluency.
2. Diagnostic Boundaries & Constraints
You must dynamically adapt your diagnostic depth based on the comparability of the two traces. You must strictly adhere to the following boundaries:
- Hardware Discrepancy Constraint: Absolute duration (ms) comparisons across different physical devices are invalid. When device models or OS differ, you must issue a prominent warning, restrict your analysis to metrics changes and long tasks.
- Framework Alignment Constraint: If metrics cannot be aligned (indicating different underlying frameworks or mismatched scenarios), you must halt the rendering metrics analysis and pivot entirely to the universal Long Task analysis.
- DSL/Stack Alignment Constraint: If metrics align but the underlying sub-event stacks differ fundamentally (indicating a DSL or architecture change), you must not compare call stack details.
- Call Stack Comparison: If the stacks match, you MUST perform a detailed call stack comparison to pinpoint the exact root cause of the degradation or improvement.
3. Analysis Focus Areas
Metrics Comparison
Wait for Update (Mandatory)
For each timing_flag present in the metrics, you must calculate the "Wait for Update" duration and compare all metrics, focusing on the following core areas: You need to determine the trigger source and duration difference based on pipeline origin for every timing_flag.
- Native Triggered (
updateGlobalProps/updateTriggeredByNative/reloadBundleFromNative): - Calculation: Wait for Update Duration =
pipelinestart time -loadBundleend time. - Verdict: If this phase degrades or improves (on same device), it indicates the client triggered the update late. The conclusion points to Native logic degradations or improvements.
- Frontend/BTS Triggered (
updateTriggeredByBts/reloadBundleFromBts/setNativeProps): - Calculation: Wait for Update Duration =
pipelinestart time -loadBackgroundend time. - Verdict: If this phase degrades or improves, query preceding trace data triggered by
diffVdom(usingflowid) to trace back the preceding chain (e.g., NativeModule calls slowing down). - Example Input JSON Snippet:
[
{
"timing_flags": "Lynx FCP",
"origin": "loadBundle",
"details": [
{ "metrics_name": "loadBackground", "end_ts_ms": "46420290.34ms" }
]
},
{
"timing_flags": "lynx_actual_fmp",
"origin": "updateTriggeredByBts",
"start_ts_ms": "46420317.43ms",
}
]Step-by-Step Execution:
- Identify Origin: For the
lynx_actual_fmpblock, theoriginis"updateTriggeredByBts"(Frontend Trigger). - Select Formula:
pipelinestart time -loadBackgroundend time. - Calculate:
46420317.43 - 46420290.34 = 27.09ms. - Result: The
Wait for Updateforlynx_actual_fmpis27.09ms. You MUST output this exact math result in the Metric Comparison table.
Metrics Comparison
For aligned metrics within each timing_flag, you must pinpoint the exact cause of degradation by evaluating:
loadBackground: BTS size expansion or CodeCache misses.create_vdom/resolve: DOM node volume and CSS parsing complexity.diffVdom: Component over-rendering (evidenced byComponent::Diffcall counts).layout: List size expansion (Layout::Measurecount) or specific component layout complexity.
Fluency & Long Tasks
- Fluency & Long Tasks (Universal): Extract and analyze Long Tasks (Main > 16ms, JS > 30ms). You must trace UI-blocking tasks back to their specific business components via
flowdata, and identify pure business logic bottlenecks (e.g., slowfetchcallbacks).
4. Output Requirements
The output MUST strictly follow this Markdown template:
1. Executive Summary
A 2-3 sentence conclusion identifying the primary bottleneck or root cause. [⚠️ WARNING: Cross-Device Comparison Detected. Absolute duration diffs are hardware-dependent.] [Example: Overall duration degrades by +150ms, primarily driven by a +100ms degradation in diffVdom due to FeedList over-rendering.]
2. Metric Comparison
(Constraint: `Wait for Update` MUST be the first row, even if normal. If frameworks do not align, output: "No aligned metrics, skipped.")
| Metric | Baseline (ms) | Experiment (ms) | Diff (ms) | Diff (%) | Status |
|---|---|---|---|---|---|
Wait for Update | [X] | [Y] | [Z] | [%] | 🟢/🔴 |
[Metric Name] | [X] | [Y] | [Z] | [%] | 🟢/🔴 |
3. Root Cause Analysis
For top degradations or top long tasks, provide a detailed analysis (Constraint: Max 3 lines per regression): [Stage/Task Name]: +[X]ms
- Evidence: [Exact Count/Depth/Duration changes, e.g.,
Component::DiffCount: 50 -> 120] - Context: [Specific component name or trigger source, e.g.,
FeedCardstate update] - Conclusion: [1 sentence root cause, e.g., Over-rendering increased DOM Diff complexity]
Lynx Jank Analysis Guide
Core Knowledge Base
1. Thread Models
- Main Thread: Always executes UI Operations (
UIOperation,Painting,ScrollByInternal). - Scenario A (Merged): Also executes
TemplateAssembler,LoadTemplate/LoadBundle. - Scenario B (Separated):
LayoutandTemplateAssemblerrun on a dedicated Layout/Engine Thread. - Lynx_JS Thread: Executes Logic (
diffVdom,EventHandler,NativeModuleetc).
2. Thresholds
- Main Thread Long Task: > 16ms (1 frame at 60fps).
- JS Thread Long Task: > 30ms (User-perceptible delay).
Diagnostic Protocol (The "Drill-Down Loop")
For every analysis, you must execute this loop:
Step 1: Identify Thread Model & Long Tasks
1. Call query_threads to get thread infos. 2. Call query_long_tasks on Main, Lynx_JS, and (if present) Layout/Engine threads.
Step 2: Deep Dive & Attribution (The "Why")
For each Long Task, call query_descendants and apply the specific logic below:
---
Diagnostic Logic & Rules (Detailed)
Phase: Main/Engine Thread - Rendering Blocking
Symptom: Long task on Main (or Layout) Thread > 16ms. Target Events: TemplateAssembler::CallLepusMethod (Update), LoadTemplate (First Frame), ScrollByInternal (Scroll), Layout. Analysis: Refer to render-pipeline for details.
Phase: Lynx_JS Thread - Diff Blocking
Symptom: Long duration between diffVdomStart and diffVdomEnd. Investigation Action: Check for ReactLynx::diff::XXXX sub-events. Analysis: Refer to render-pipeline diffVdom section for details.
Phase: Lynx_JS Thread - NativeModule Blocking
Symptom: Long task contains NativeModule::Invoke. Verdict Logic:
- Frequent Calls: Multiple calls to the same module in a short time.
- Action: Batch NativeModule calls.
- Single Slow Call: One call takes > 30ms.
- Action: Refer to nativemodule-analysis for details.
Phase: Cross-Instance Blocking
Symptom: Long task on JS thread has a different instance_id than the current page. Verdict: Thread Contention (Other LynxView blocking shared JS thread). Action: Enable Multi-JS Threads (LynxGroup) to isolate instances.
Phase: Unknown JS Blocking
Symptom: Long task on JS thread has NO specific sub-events (just "RunningInJS" or similar). Verdict: Heavy JS Execution (Unknown Function). Action: Suggest enabling JS Profile and re-recording trace to pinpoint the exact function.
---
Output Requirements
1. Smoothness Summary A 2-3 sentence conclusion identifying the primary bottleneck (Main Thread Visual Stutter vs. JS Thread Input Lag) and the root cause.
2. Long Task Analysis Table
| Thread | Event Name | Duration (ms) | Root Cause |
|---|---|---|---|
| Main | Layout | 45.2 | Text Measurement |
| Lynx_JS | diffVdom | 32.0 | Large List Diff (Component: FeedList) |
3. Deep Dive & Recommendations For the top bottleneck, provide specific analysis:
- Investigating [Event Name]:
- Sub-event Evidence: "Found
ReactLynx::diff::FeedItemtaking 25ms." - Conclusion: "Component
FeedItemis re-rendering too often." - Action: "Wrap
FeedIteminReact.memo."
4. Prioritized Suggestions Provide 2-5 actionable recommendations sorted by priority.
- If Rspeedy Profile is missing: High Priority recommendation to enable it.
- If Cross-Instance: High Priority recommendation to enable Multi-JS.
Lynx Metrics Analysis Guide
Core Knowledge Base
Before analyzing metrics, you must understand the Lynx rendering pipeline. Loading render-pipeline.md for more details. ---
Analysis Steps
Step 1: Reading Render Pipeline
1. Read render-pipeline to understand the Lynx rendering pipeline.
Step 2: Retrieve Metrics
1. Call query_metrics to retrieve First Frame and/or Update stage metrics. 2. If the query_metrics result contains "Invalid Timing Flags": Read [timing-flag](./timing-flag.md) and follow the troubleshooting steps in the guide to diagnose why the flags are invalid 3. Identify suspicious stages based on absolute duration and ratio within the parent.
Step 3: Deep Dive Suspicious Stages
1. For each suspicious stage or gap, according to render-pipeline to query trace data. 2. Analyze the trace data to identify the root cause of the slow stage or gap.
Step 4: Generate Output
1. Generate the final output report based on the analysis results. ---
Output Requirements
1. Executive Summary A 2-3 sentence conclusion identifying the primary bottleneck (Stage, Gap, or Trigger Latency). Example: "Update rendering took 1080ms. The main bottleneck is trigger latency (800ms) caused by a slow NativeModule(id) request before `diffVdom(id)` started."
2. Metric Breakdown Table
| Stage/Gap | Duration (ms) | Ratio | Analysis |
|---|---|---|---|
loadBundle(id) | 600 | 100% | |
| `layout(id)` | 300 | 50% | Complex Text Layout |
| [GAP] | 50 | 8% | GC Blocking |
3. Render Pipeline Overview A short, narrative description (3–6 sentences) of how this page was rendered/updated:
- First-frame flow: which stages ran, how long they took, and any important inter-stage gaps.
- Update flow (if relevant): what triggered the update, timing of subsequent events, and trigger latency analysis.
4. Prioritized Suggestions Provide 2-5 actionable recommendations sorted by priority.
- If CodeCache missing: High Priority recommendation to enable it.
- If Trigger Latency: Optimize the identified precursor (NativeModule, async task, etc.).
Requirements
- Optimization Recommendations must be based on the Lynx rendering pipeline defined in ./render-pipeline.
- Analysis must follow the steps outlined in the Analysis Steps section.
Lynx NativeModule Performance Analysis
Core Knowledge Base
1. The Three Core Stages of NativeModule Calls
You must calculate durations based on these three key events:
- Event A:
NativeModule::Invoke(Start of the call). - Event B:
NativeModule::PlatformCallbackStart(Platform logic finished, callback triggering). - Event C:
NativeModule::Callback(Callback execution starts onLynx_JSthread).
Duration Formulas: 1. `T_platform` (Platform Duration) = Timestamp(B) - Timestamp(A)
- Meaning: Actual execution time of the native method (I/O, computation, network request).
2. `T_wait` (Waiting Duration) = Timestamp(C) - Timestamp(B)
- Meaning: Time spent waiting for thread scheduling, thread switching, or queueing on the
Lynx_JSthread.
3. `T_js` (JS Duration)
- Calculation: If
PubValueToJSValueandInvokeCallbackevents exist, sum their durations. Otherwise, use the duration ofNativeModule::Callback. - Meaning: Time spent converting native data to JS values and executing the JS callback logic.
Diagnostic Logic & Rules
Step 1: Data Integrity Check & Completion
Check if the input trace contains all three core events (NativeModule::Invoke, NativeModule::PlatformCallbackStart, NativeModule::Callback).
- If events are missing:
1. Select an existing core event ID. 2. Call query_flow_events(event_id) to find related events. 3. Reconstruct the chain and proceed.
Step 2: Bottleneck Identification
Compare T_platform, T_wait, and T_js. The stage with the highest duration or percentage is the Primary Bottleneck.
Step 3: Optimization Strategy (Symptom -> Action)
- Bottleneck: `T_platform` (Native Execution)
- Check: Is it a network request (args contains
fetch,x.request)? - Action (Network):
1. Check for NetworkModule.callback. If missing, suggest switching to LynxNetwork for async benefits.
- Action (Non-Network): Collaborate with Native engineers to profile the native method for synchronous I/O or heavy computation.
- Bottleneck: `T_wait` (Scheduling/Queueing)
- Investigation: Look at the
Lynx_JSthread betweenNativeModule::PlatformCallbackStartandNativeModule::Callback. - Action: Identify what blocked the thread (e.g., heavy JS loops, other NativeModule callbacks). Suggest optimizing those blocking tasks.
- Bottleneck: `T_js` (JS Execution)
- Investigation: High cost usually means large data conversion or complex callback logic.
- Action:
1. Reduce Data Payload: Ask backend/native to prune unused fields to speed up serialization. 2. Optimize Callback: Simplify JS logic inside the callback; avoid forcing reflow/layout.
Output Requirements
Structure your response exactly as follows: 1. Phase Duration Analysis
| Phase | Duration (ms) | Ratio (%) | Core Attribution |
|---|---|---|---|
| Platform | [Value] | [%] | Native method execution, Network I/O |
| Waiting | [Value] | [%] | Thread switching, JS thread queueing |
| JS Execution | [Value] | [%] | Data conversion, JS callback logic |
| Total | [Sum] | 100% |
2. Primary Bottleneck A concise statement identifying the bottleneck.
Example: "The primary bottleneck is Platform, taking 150ms (80%), likely due to slow network response."
3. Actionable Optimization Suggestions Provide specific advice based on the identified bottleneck.
Lynx Rendering Pipeline Analysis Guide
This guide provides a deep-dive analysis framework for the Lynx rendering pipeline. It covers both First Frame Rendering and Update Rendering.
Core Knowledge
1. First Frame Rendering Metrics (from-scratch render)
Top-level metric: loadBundle, with serial sub-metrics:
parse– Time to parse the binary Bundle into in-memory structures.mtsrender– Time to execute Main Thread Script to build the initial Element Tree.resolve– Time to compute styles (Computed Styles) for nodes.layout– Time to compute positions and sizes based on the resolved layout tree.paintingUiOperationExecute– Time to apply UI operations (style/layout changes) to platform UI.paint– Time spent by the platform layer to actually draw pixels.loadBackground– Time to load and execute Background Thread Script. This runs in parallel and does not block first-frame completion, but strongly affects later updates.
2. Update Rendering Metrics (refresh-on-change)
Triggered by data changes or user interactions: 1. diffVdom (background) – Compute Virtual DOM differences. 2. packChanges (background) – Serialize differences. 3. parseChanges (main) – Deserialize differences. 4. patchChanges (main) – Apply differences to the Element Tree. 5. Reuses pixel pipeline: resolve → layout → paintingUiOperationExecute → paint.
Diagnostic
First Frame Rendering
loadBundle/LynxLoadTemplate
- Definition: Lynx First Frame Rendering stage, composed by
parse,mtsrender,resolve,layout,paintingUiOperationExecute, etc stages.
parse
- Definition: Parse the Lynx bundle for subsequent pipeline processing. Includes bundle decoding and script deserialization.
- Deep Dive Logic:
- Query: Call
query_by_time_window(parse.start_ts, parse.end_ts, parse.track_id)to get trace events. - Verdict Logic:
- If
LepusNG.DeSerializeis time-consuming andbytecodeSizeis large -> MTS bundle size is too large. - If multiple pages share the same
urlbut all have highparsetime -> Lack of template reuse / Missing Pre-Decode. - Optimization:
- MTS Bundle Too Large: Reduce bundle size; split large bundles into smaller chunks; remove unused code.
- Missing Pre-Decode: Use Pre-Decode to pre-decode templates and reuse parsed bundles across multiple LynxViews.
mtsrender
- Definition: Execute Main Thread Script (MTS) to construct the element tree. Includes
vmExecute,dataProcessor, andcreate_vdomphases. - Deep Dive Logic:
- Query: Call
query_by_time_window(mtsrender.start_ts, mtsrender.end_ts, mtsrender.track_id)to get trace events. - Verdict Logic:
- If
vmExecutephase is high -> MTS size is large. - If
dataProcessorphase is high -> Complex data processing logic. - If
create_vdomphase is high -> Usequery_by_time_windowin thecreate_vdomwindow and check element count and types (e.g.,FiberCreateXXXX). Ifwrappernodes are dominant -> Too many wrapper nodes. - Optimization:
- Large MTS Size: Reduce MTS bundle size; move non-critical logic to Background Thread Script (BTS).
- Complex Data Processing: Simplify
dataProcessorlogic; keep only essential logic; defer heavy computation. - Too Many Wrapper Nodes: Upgrade ReactLynx3 toolchain (≥ 0.100.0) to enable "Automatic Wrapper Removal"; simplify component hierarchy.
resolve
- Definition: Resolve element attributes (properties, events, styles) and synchronize computed styles to layout nodes.
- Deep Dive Logic:
- Query: Call
query_by_time_window(resolve.start_ts, resolve.end_ts, resolve.track_id)to get trace events. - Verdict Logic:
- If you see many wrapper-like nodes (args.tagName is
wrapper) -> Excessive wrapper components. - Optimization:
- Excessive Wrappers: Reduce wrapper components and unnecessary nesting; upgrade toolchains that automatically remove wrappers.
paintingUiOperationExecute
- Definition: Execute platform UI operations (create, insert, update, delete platform UI elements).
- Deep Dive Logic:
- Query: Call
query_by_time_window(paintingUiOperationExecute.start_ts, paintingUiOperationExecute.end_ts, paintingUiOperationExecute.track_id)to examine the density of platform UI operations and any synchronous resource/image operations. - Verdict Logic:
- If
Interceptor.shouldRedirectImageUrlappears in this window -> Synchronous image URL redirect blocking UI operations. - If image synchronous fetch events appear in this window -> Synchronous image loading blocking UI operations.
- Optimization:
- Image URL Redirect: Enable Asynchronous Image Redirecting to avoid blocking the main thread.
- Synchronous Image Loading: Enable Asynchronous Image Fetching.
loadBackground
- Definition: Time to load and execute Background Thread Script (BTS). Runs in parallel with the main pipeline but is critical for subsequent updates.
- Deep Dive Logic:
- Query: Call
query_by_time_window(loadBackground.start_ts, loadBackground.end_ts, loadBackground.track_id)to get trace events. - Verdict Logic:
- If
evaluateJavaScriptBytecodeexists -> Bytecode Active (Slow due to script size/logic). - If
evaluateJavaScriptBytecodeMISSING ANDJsCacheManager::TryGetCacheMISSING -> No Opts Active (Needs Bytecode/CodeCache). - If
JsCacheManager::TryGetCachepresent but followed byevaluateJavaScript-> CodeCache Miss. - Optimization:
- If the platform is iOS/Darwin: You may only reason about Bytecode distribution strategy and script size / logic placement.
- Else:
- If Needs Bytecode/CodeCache:
- Recommend open Bytecode or CodeCache to skip parse+compile stage.
- If CodeCache Miss:
- Recommend record a new trace with DevTools disabled to confirm whether CodeCache hits in a production environment or check online
lynxsdk_code_cachehit-rate metrics to see whether misses are systemic. - If Bytecode Active but still slow:
- Recommend focus on reducing Background Thread Script size and complexity; defer non-critical work outside the
loadBackgroundwindow, and/or load logic lazily where possible.
Update Rendering
diffVdom
- Definition: Compare the new virtual DOM with the old one to determine minimal changes needed. Runs on Lynx_JS Thread.
- Deep Dive Logic:
- Investigation Action: Call
query_by_time_window(diffVdom.start_ts, diffVdom.end_ts, diffVdom.track_id)to get sub-events, and check forReactLynx::diff::XXXXsub-events. - Verdict Logic:
- Scenario A: No `ReactLynx::diff::XXXX` events found:
- Verdict: Rspeedy Performance Profile NOT Enabled.
- Action: Advise user to enable profile in
lynx.config.tsand re-record trace:
export default defineConfig({ performance: { profile: true } });- Scenario B: `ReactLynx::diff::XXXX` events found:
- Verdict: Identify the specific component name (e.g.,
ReactLynx::diff::MyComponent). - Action: Optimize the identified component using
useMemoorshouldComponentUpdateto skip unnecessary diffs. - Optimization Summary:
- If Rspeedy Profile Missing: High Priority recommendation to enable it for precise component-level diagnosis.
- If Component Identified: Optimize specific component with
React.memo,useMemo, orshouldComponentUpdate. - If Deep Component Tree: Flatten component hierarchy; use memoization for expensive components.
Update Trigger Latency Analysis
When origin indicates an update-triggered pipeline (e.g., updateTriggeredByBts, updateTriggeredByNative, updateGlobalProps, setNativeProps):
1. For updateTriggeredByBts specifically, call query_flow_events anchoring on the diffVdom event, and trace backwards along the flow to find its immediate precursors (e.g., Background Thread Script work, NativeModule responses, timers/callbacks, resource readiness, UpdateData etc.). 3. For host-driven origins (updateTriggeredByNative, updateGlobalProps, setNativeProps), inspect client-side investigation required to determine why the trigger timing is late.
Deep Dive Logic
- Attribute late update-start to the concrete precursor types you actually observe (e.g., slow NativeModule response before
diffVdom, heavy background computation, delayed host-triggered calls). - Map actions accordingly (optimize the relevant NativeModule, move heavy logic off critical path, reduce data size, or adjust update frequency).
SQL Guide for Trace Analysis
This guide provides instructions on how to write SQL queries for analyzing trace data using the Perfetto trace processor.
Table of Contents
- Introduction
- Core Tables
- Slice and Counter Tables
- Thread and Process Tables
- Metadata and Arguments Tables
- Common SQL Queries
- Basic Queries
- Time-based Queries
- Aggregation Queries
- Joining Tables
- Advanced Techniques
- Working with Arguments
- Time Conversions
- Subqueries and CTEs
- Built-in Functions and Views
- Flow Analysis Functions
- Hierarchy Functions
- Table Relationships
- Best Practices
- Example Queries from Real Traces
Introduction
Perfetto's trace processor loads trace data into a set of SQL tables, enabling powerful and flexible analysis using standard SQL. This guide explains the key tables, relationships, and patterns to help you write effective queries for performance debugging, bottleneck identification, and system behavior analysis.
Core Tables
Slice Tables
The slice table contains all duration-based events (e.g., function calls, layout phases), while the counter table holds instantaneous numeric samples (e.g., CPU frequency, memory usage).
slice Table
| Column | Type | Description |
|---|---|---|
id | INTEGER | Unique slice identifier |
ts | INTEGER | Start timestamp (nanoseconds) |
dur | INTEGER | Duration (nanoseconds) |
track_id | INTEGER | Track this slice belongs to |
name | TEXT | Slice name |
depth | INTEGER | Nesting depth (0 = top‑level) |
arg_set_id | INTEGER | Reference to argument set in args table |
Thread and Process Tables
These tables provide context for tracks and slices.
thread Table
| Column | Type | Description |
|---|---|---|
utid | INTEGER | Unique thread ID (internal) |
upid | INTEGER | Unique process ID (parent) |
tid | INTEGER | OS thread ID |
name | TEXT | Thread name |
arg_set_id | INTEGER | Arguments for this thread |
process Table
| Column | Type | Description |
|---|---|---|
upid | INTEGER | Unique process ID (internal) |
pid | INTEGER | OS process ID |
name | TEXT | Process name |
uid | INTEGER | User ID |
arg_set_id | INTEGER | Arguments for this process |
Track Tables
thread_track: mapstrack_id(fromslice) to a thread (utid).process_track: mapstrack_idto a process (upid).
| Table | Key Columns |
|---|---|
thread_track | id (track_id), utid |
process_track | id (track_id), upid |
Metadata and Arguments Tables
metadata: trace‑level information (e.g., system name, tracing timestamps).args: stores key‑value arguments attached to slices, counters, threads, etc.
metadata Table
| Column | Type | Description |
|---|---|---|
name | TEXT | Metadata key |
str_value | TEXT | String value (if any) |
int_value | INTEGER | Integer value (if any) |
args Table
| Column | Type | Description |
|---|---|---|
arg_set_id | INTEGER | References arg_set_id from other tables |
key | TEXT | Argument name (often prefixed with "debug.") |
display_value | TEXT | Human‑readable value |
💡 Many tables contain anarg_set_idcolumn; join withargsusing that field to retrieve detailed event attributes.
Common SQL Queries
Basic Queries
-- First 10 slices
SELECT * FROM slice LIMIT 10;
-- Slices with a specific name
SELECT * FROM slice WHERE name = 'LynxEngine::LoadTemplate' LIMIT 10;
-- Slices on a given track
SELECT * FROM slice WHERE track_id = 123 LIMIT 10;Time-based Queries
-- Slices within a time window (nanoseconds)
SELECT * FROM slice
WHERE ts BETWEEN 1000000000 AND 2000000000
LIMIT 10;
-- Long slices (duration > 10 ms)
SELECT * FROM slice
WHERE dur > 10 * 1e6
LIMIT 10;Aggregation Queries
-- Count slices by name
SELECT name, COUNT(*) AS count
FROM slice
GROUP BY name
ORDER BY count DESC;
-- Average duration by name
SELECT name, AVG(dur) AS avg_duration_ns
FROM slice
GROUP BY name
ORDER BY avg_duration_ns DESC;Joining Tables
-- Slice with thread name
SELECT s.id, s.name, s.ts, t.name AS thread_name
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t ON tt.utid = t.utid
LIMIT 10;Advanced Techniques
Working with Arguments
Arguments are stored in the args table, keyed by arg_set_id. To include them in your results:
-- Slices with a specific argument key
SELECT s.id, s.name, a.key, a.display_value
FROM slice s
JOIN args a ON s.arg_set_id = a.arg_set_id
WHERE a.key = 'debug.bundle_url'
LIMIT 10;
-- Group all arguments of a slice into a JSON-like string
SELECT s.id, s.name, json_group_object(a.key, a.display_value) AS args
FROM slice s
LEFT JOIN args a ON s.arg_set_id = a.arg_set_id
GROUP BY s.id
LIMIT 10;Time Conversions
Timestamps and durations are in nanoseconds. Convert to milliseconds by dividing by 1e6:
SELECT id, name,
ts / 1e6 AS ts_ms,
dur / 1e6 AS dur_ms
FROM slice
LIMIT 10;Subqueries and CTEs
-- Find slice names that appear more than 10 times
WITH freq AS (
SELECT name, COUNT(*) AS cnt
FROM slice
GROUP BY name
)
SELECT name, cnt
FROM freq
WHERE cnt > 10
ORDER BY cnt DESC;Built-in Functions and Views
Perfetto provides special table‑valued functions for analyzing flow events and slice hierarchies.
Flow Analysis Functions
These functions help trace data flow between slices (e.g., task scheduling, IPC).
directly_connected_flow(slice_id): returns immediate predecessors and successors.preceding_flow(slice_id): returns all slices that flow into or out of the given slice (transitive).
-- Find all slices that flow into slice 123
SELECT slice_in AS slice_id
FROM directly_connected_flow(123);
-- Find all slices that precede slice 123
SELECT slice_in AS slice_id
FROM preceding_flow(123);Hierarchy Functions
ancestor_slice(slice_id): returns the slice itself and all its ancestors (parent, grandparent, …).descendant_slice(slice_id): returns the slice itself and all its descendants (children, grandchildren, …).
Each returns rows with the same schema as the slice table.
-- All ancestors of slice 123, from topmost to the slice itself
SELECT id, name, depth
FROM ancestor_slice(123)
ORDER BY depth ASC;
-- All descendants of slice 123, from the slice itself to deepest child
SELECT id, name, depth
FROM descendant_slice(123)
ORDER BY depth DESC;Table Relationships
Table Relationships
Understanding how tables link is essential for correct joins.
| Source Table | Source Field | Target Table | Target Field | Description |
|---|---|---|---|---|
slice | track_id | thread_track | id | Slice belongs to a thread track |
slice | arg_set_id | args | arg_set_id | Slice has arguments |
thread_track | utid | thread | utid | Thread track is owned by a thread |
process_track | upid | process | upid | Process track is owned by a process |
thread | upid | process | upid | Thread belongs to a process |
Best Practices
1. Always use LIMIT when exploring to avoid large result sets. 2. Select only needed columns – SELECT * can be heavy, especially with many arguments. 3. Leverage indexes: The slice table is indexed on ts and track_id; filter on these when possible. 4. Avoid heavy joins on large traces – if you need thread/process names, consider creating a temporary table with pre‑joined data. 5. Use aggregate functions (COUNT, AVG, SUM) to summarize instead of dumping raw events. 6. Convert units (ns → ms) early in the query for readability. 7. Test with small time windows before expanding to full trace. 8. Use CTEs to break complex queries into readable steps.
Example Queries from Real Traces
Slice by ID with thread name and arguments
SELECT
s.id,
s.ts,
s.dur,
s.track_id,
s.name,
t.name AS thread_name,
json_group_object(a.key, a.display_value) AS args
FROM slice s
LEFT JOIN args a ON s.arg_set_id = a.arg_set_id
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t ON tt.utid = t.utid
WHERE s.id = 381;Slices in a time window with depth and thread (excluding a noisy argument)
SELECT
s.id,
s.track_id,
s.ts,
s.dur,
s.name,
s.depth,
t.name AS thread_name,
json_group_object(a.key, a.display_value) AS args
FROM slice s
LEFT JOIN args a ON s.arg_set_id = a.arg_set_id
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t ON tt.utid = t.utid
WHERE s.ts BETWEEN 1000000000 AND 2000000000
AND a.key != 'debug.url'
GROUP BY s.id
ORDER BY s.depth, s.ts;Aggregate slice statistics over a 5‑second window
SELECT
s.name,
COUNT(*) AS total_count,
SUM(dur) AS total_duration_ns,
AVG(dur) AS avg_duration_ns,
MAX(dur) AS max_duration_ns
FROM slice s
WHERE s.ts BETWEEN 0 AND 5e9
GROUP BY s.name
ORDER BY total_duration_ns DESC;For more details, refer to the official Perfetto SQL tables documentation.
Lynx Timing Flag Analysis Guide
Core Knowledge
1. Timing Flag Concepts
- Purpose: A string identifier (
__lynx_timing_flag) used to tag and monitor a specific rendering pipeline. - Render Cycles:
- Setup: Initial render when the LynxView loads (happens once).
- Update: Re-render triggered by data/attribute changes.
2. Injection Methods
- Attribute Flag (Recommended): Added directly to the component (e.g.,
<view __lynx_timing_flag="my_flag">). Only triggers if the component is mounted/rendered with a valid, non-empty, previously unused flag. - setState Flag (Legacy): Passed inside
setState. Rule: If the data update causes NO actual UI change, the flag is ignored. - Native Injection: Injected via
updateDataor container timestamps viasetExtraTiming(crucial forTotal_FCPandTotal_ActualFMP).
Diagnostic Protocol (Symptom -> Action -> Verdict)
When a user reports a Timing Flag issue, apply the following logic:
Scenario 1: Missing onSetup Callback
- Symptom: The initial render callback is not firing.
- Investigation Action: Search the trace for the event
TimingMediator::TriggerSetupRuntimeCallback. - Verdict Logic:
- If the event is MISSING -> The
PerformanceObserverwas registered too late. - Recommendation: Advise the user to register the observer earlier (e.g., in
useMemoor the class constructor) before the flagged render occurs.
Scenario 2: Missing onUpdate Callback (Using Attribute Flag)
- Symptom:
__lynx_timing_flagis set on a component, but no update callback fires. - Investigation Action 1 (Check Validity): Execute the following SQL to find Timing events:
SELECT
GROUP_CONCAT(CASE WHEN a.key = 'debug.timing_flags' THEN a.display_value ELSE NULL END) as timing_flags,
s.id, s.name, s.ts / 10e6 as ts_ms
FROM slice s JOIN args a ON s.arg_set_id = a.arg_set_id
WHERE a.display_value != 'react_lynx_hydrate' and s.name Like 'Timing::%'
GROUP BY s.id, s.name, s.arg_set_id
HAVING MAX(CASE WHEN a.key = 'debug.timing_flags' THEN 1 ELSE 0 END) = 1
ORDER BY s.ts;- Verdict Logic 1: If the target flag is NOT in the SQL results -> Invalid Flag. The flag might be empty, null, undefined, or placed on an unsupported element (like
<template>or<block>). - Investigation Action 2 (Check Listener): If the flag IS in the SQL results, search the trace for
TimingMediator::TriggerUpdateRuntimeCallback. - Verdict Logic 2: If the event is MISSING -> Late Registration. The listener was not ready when the pipeline finished.
Scenario 3: Missing onUpdate Callback (Using setState)
- Symptom: Flag passed via
setState, but no callback fires. - Investigation Action: Run the SQL query from Scenario 2. Check if the event
Timing::Mark::paintEndexists for this specific flag. - Verdict Logic: If
paintEndis MISSING -> No UI Update. ThesetStatedid not result in a UI change (e.g., diff bailed out). - Recommendation: Ensure actual data that changes the UI is updated alongside the flag.
Scenario 4: LynxActualFMP is Abnormally Large (e.g., > 10 seconds)
- Symptom: The duration for
__lynx_timing_actual_fmpis unrealistically long. - Investigation Action: Run the SQL query from Scenario 2 to find the
Timing::Mark::paintEndevent for this specific flag. Identify the event preceding it in this flag's pipeline, and calculate the time gap between them. - Verdict Logic:
- If there is a large gap with massive blank/idle time -> Delayed UI Update. The flag was set, but the UI did not actually update at that moment. The pipeline waited, and
paintEndwas only triggered much later when a subsequent, unrelated UI update forced a paint. - If there is a large gap filled with heavy tasks -> Main Thread Blocked. Rendering was delayed by other long-running operations blocking the UI thread.
- Recommendation: If "Delayed UI Update" is detected, ensure the data update (
setStateor attribute change) tied to the flag actually causes an UI change. If the data doesn't change the UI, the flag will hang.
Scenario 5: TotalActualFMP is Abnormally Large (e.g., > 10 seconds)
- Symptom: The duration for
__lynx_timing_actual_fmpis unrealistically long. - Investigation Action: Check
open_time(container timestamp); Check if the page was preloaded. - Verdict Logic:
- If
open_timeis 0 or missing -> Missing Container Timestamp. Native client failed to callsetExtraTimingbeforerenderTemplate. - If it's a Preload scenario -> The time includes background idle time.
- Recommendation: Coordinate with Native engineers to call
setExtraTimingcorrectly, or recalculate ActualFMP starting from the LynxView display time for preloaded pages.
Best Practices to Enforce
If you notice poor implementation in the user's code or trace, proactively recommend these: 1. Prefer Attribute Flags: Stop using setState for flags; use __lynx_timing_flag on components. 2. Long List Anti-Pattern: If tracking a list, place ONE flag on the <list> component itself. If you must flag items, use the SAME flag name for all items. Using unique names per item causes a callback storm and severe performance degradation. 3. Meaningful Naming: Use __lynx_timing_actual_fmp specifically for the element that indicates the page is ready for the user.