Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion profiler-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ profiler-cli thread samples-top-down # Show top-down call tree (where CPU
profiler-cli thread samples-bottom-up # Show bottom-up call tree (what calls hot functions)
profiler-cli thread markers # List markers with aggregated statistics [--list for flat per-marker view]
profiler-cli thread functions # List all functions with CPU percentages
profiler-cli thread network # Show network requests with timing phases [--search] [--min-duration] [--max-duration] [--limit]
profiler-cli thread network # Show network requests with timing phases [--search] [--min-duration] [--max-duration] [--limit] [--sort]
profiler-cli thread page-load # Show page load summary (navigation timing, resources, CPU, jank)
profiler-cli marker info <handle> # Show detailed marker information (e.g., m-1234)
profiler-cli marker stack <handle> # Show full stack trace for a marker
Expand Down Expand Up @@ -86,6 +86,7 @@ profiler-cli thread info --thread t-0 # View info for specific thread witho
| `--json` | Output as JSON (for use with `jq`, etc.) |
| `--limit <N>` | Limit number of results shown |
| `--max-lines <N>` | Limit call tree nodes for `samples-top-down`/`samples-bottom-up` (default: 100) |
| `--sort <order>` | Order `thread network` requests: `duration` (default, slowest first) or `start` (chronological) |
| `--scoring <strategy>` | Call tree scoring: `exponential-0.95`, `exponential-0.9` (default), `exponential-0.8`, `harmonic-0.1`, `harmonic-0.5`, `harmonic-1.0`, `percentage-only` |
| `--navigation <N>` | Select which navigation to show in `thread page-load` (1-based, default: last completed) |
| `--jank-limit <N>` | Max jank periods to show in `thread page-load` (default: 10, 0 = show all) |
Expand Down
19 changes: 17 additions & 2 deletions profiler-cli/guide.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ CORE WORKFLOW
profiler-cli profile info --search GeckoMain Filter by process/thread name, pid, or tid
profiler-cli profile logs Print all Log markers in MOZ_LOG format (across all threads)

See the "Network activity" section in "profile info": if requests are in
flight for much of the profile while threads are idle, the bottleneck may be
the network -- follow the m-N handles (zoom push / marker info) to dig in.

Step 3: Select a thread to analyze
profiler-cli thread select t-0 Select a specific thread (persists for future commands)

Expand Down Expand Up @@ -68,7 +72,8 @@ CORE WORKFLOW
profiler-cli thread markers --list Flat chronological list (one row per marker)
profiler-cli thread markers --search X --list List all individual X markers with handles

profiler-cli thread network First 20 requests with timing phases (default)
profiler-cli thread network Top 20 requests (slowest first) with timing phases
profiler-cli thread network --sort start Chronological order instead of slowest-first
profiler-cli thread network --limit 0 All requests (no limit)
profiler-cli thread network --search "api.example" Filter by URL substring
profiler-cli thread network --min-duration 200 Only requests >= 200ms
Expand Down Expand Up @@ -320,7 +325,8 @@ COMMON ANALYSIS PATTERNS
Marker handles shown in page-load output can be passed to "marker info" or "zoom push".

Analyze network activity:
profiler-cli thread network All requests with timing phases
profiler-cli thread network Top requests, slowest first, with timing phases
profiler-cli thread network --sort start Chronological order instead
profiler-cli thread network --min-duration 200 Slow requests only (>= 200ms)
profiler-cli thread network --search "api" Filter by URL substring
profiler-cli thread markers --category Network Cross-reference with marker view
Expand Down Expand Up @@ -349,10 +355,19 @@ UNDERSTANDING THE OUTPUT
samples-bottom-up Hot leaf functions at top with their callers.
Use to understand what calls a frequently-seen function.

Network summary ("thread network"):
"In flight" is the wall-clock signal: time at least one request was open
(overlaps counted once) plus peak concurrency. "Phase totals" are summed
across concurrent requests, so they can exceed the wall clock -- judge
network stall by "In flight", not the phase sum.

TIPS

- Start with "profile info"; GeckoMain is usually the main thread -- use
"--search GeckoMain" to find it quickly
- CPU time is not the only signal. A profile where threads are mostly idle but
network is in flight for most of the duration is a network-bound profile --
report the slow requests, not the hottest function.
Comment on lines +368 to +370

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat - very specific but probably useful

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I wanted to make this explicit because it was still ignoring the network some times when I was testing with some profiles. This improved that.

- Idle time alone is not a finding: only investigate idle during a period when the
thread should be busy (e.g. inside a zoom on a jank marker), which may indicate
lock contention or blocking on another thread
Expand Down
34 changes: 33 additions & 1 deletion profiler-cli/schemas.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,27 @@ profiler-cli profile info --json
counters?: [CounterSummary]
}],
remainingProcesses?: { count, combinedCpuMs, maxCpuMs },
networkActivity: ProfileNetworkSummary | null,
context: SessionContext
}

NetworkSummaryRequest:
{
markerHandle, threadHandle, url,
durationMs, startTime,
transferSizeKB?, httpStatus?, status,
incomplete, startedBeforeRecording
}

ProfileNetworkSummary:
{
requestCount, incompleteCount,
inFlightMs, inFlightPercentage,
peakConcurrency, errorCount, rangeDurationMs,
slowest: [NetworkSummaryRequest],
byThread: [{ threadHandle, threadName, requestCount, inFlightMs }]
}

CounterSummary:
{
counterHandle, counterIndex, name, label, category,
Expand Down Expand Up @@ -67,9 +85,19 @@ profiler-cli thread info --json
sampleCount, markerCount,
cpuActivity: [{ startTime, startTimeName, startTimeStr,
endTime, endTimeName, endTimeStr, cpuMs, depthLevel }] | null,
networkActivity: ThreadNetworkSummary | null,
context: SessionContext
}

ThreadNetworkSummary:
{
threadHandle, threadName,
requestCount, incompleteCount,
inFlightMs, inFlightPercentage, peakConcurrency,
errorCount, cacheHit, cacheMiss, cacheUnknown, rangeDurationMs,
slowest: [NetworkSummaryRequest]
}

profiler-cli thread samples --json
{
type: "thread-samples",
Expand Down Expand Up @@ -150,15 +178,19 @@ profiler-cli thread network --json
type: "thread-network",
threadHandle, friendlyThreadName,
totalRequestCount,
incompleteCount,
filteredRequestCount,
sort: "duration" | "start",
filters?: { searchString?, minDuration?, maxDuration?, limit? },
summary: {
cacheHit, cacheMiss, cacheUnknown,
inFlightMs, inFlightPercentage, peakConcurrency, rangeDurationMs,
phaseTotals: { dns?, tcp?, tls?, ttfb?, download?, mainThread? }
},
requests: [{
markerHandle, url, httpStatus?, httpVersion?, cacheStatus?,
transferSizeKB?, startTime, duration,
transferSizeKB?, startTime, duration, status,
incomplete, startedBeforeRecording,
phases: { dns?, tcp?, tls?, ttfb?, download?, mainThread? }
}],
context: SessionContext
Expand Down
12 changes: 12 additions & 0 deletions profiler-cli/src/commands/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,17 +304,29 @@ export function registerThreadCommand(
'Filter by maximum total request duration in milliseconds'
)
.option('--limit <N>', 'Max requests to show (default: 20, 0 = show all)')
.option(
'--sort <order>',
'Sort requests by "duration" (default, slowest first) or "start" (chronological)'
)
).action(async (opts) => {
const networkFilters: {
searchString?: string;
minDuration?: number;
maxDuration?: number;
limit?: number;
sort?: 'start' | 'duration';
} = {};

if (opts.search !== undefined) {
networkFilters.searchString = opts.search;
}
if (opts.sort !== undefined) {
if (opts.sort !== 'start' && opts.sort !== 'duration') {
console.error('Error: --sort must be "start" or "duration"');
process.exit(1);
}
networkFilters.sort = opts.sort;
}
if (opts.minDuration !== undefined) {
networkFilters.minDuration = parseFloatArg(
'--min-duration',
Expand Down
Loading
Loading