5.4.1 Timing Profile Analysis
A timing profile answers a more useful question than “why was this build slow?”: which kind of work occupied the elapsed time in this particular invocation? Read it as a timeline first, then turn visible spans into hypotheses about loading, analysis, execution, caching, or remote work. The profile is evidence from one run, not a permanent description of the target.
Capture a comparable profile
Use an explicit path so that the artifact you inspect is unambiguous:
bazel build //app:release --profile=release-profile.json.gz
Current Bazel releases also generate profiles for build-like commands and queries by default, under the output base. That behavior, the retained-file count, and the available profile rows are version-sensitive. An explicit --profile path is therefore the safer choice for an investigation that others must reproduce.1,2
Record the Bazel version, revision, targets, flags, machine or worker class, and cache state beside the file. More importantly, name the workload:
- cold-server build: start from a newly started Bazel server.
- warm no-op build: repeat the same command without an edit.
- warm incremental build: make one representative edit, then rebuild.
These are different experiments. A clean or cold build cannot explain a complaint about edit-build latency. Likewise, two profiles captured with different configurations or download policies are not a controlled comparison. The in-memory analysis state belongs to the Bazel server. A restart, clean, or a configuration change can prevent a later invocation from reusing it.3
Open the compressed profile in a trace viewer supported by your environment.
The official documentation describes chrome://tracing. Perfetto is another
commonly used viewer for Chrome trace data.1,4 A JSON trace is a collection
of timestamped events arranged into process and thread lanes. Horizontal
position is time and width is observed duration. Overlap across different
thread lanes can show concurrent work; overlap within one thread can instead be
nested parent/child instrumentation that describes the same interval at two
levels. Do not add overlapping span widths to estimate elapsed time or consumed
capacity. Start zoomed out. Find the main-thread phase markers, then inspect the
busy rows beneath the long phase rather than immediately selecting the longest
colored box.
Read rows as observations, not phase boundaries
2.2.1 Loading, Analysis & Execution supplies the conceptual loading, analysis, and execution model. A trace viewer does not reproduce that model as three perfectly separated bands. Work can overlap, events can be aggregated, and the special rows included in a profile depend on the Bazel version and profiler flags.1
Some profiles contain worker rows named like skyframe evaluator 0 and skyframe evaluator execution-0. In profiles where those labels appear, the evaluator rows are useful places to inspect graph evaluation, while the execution variants contain action-execution work.3 Treat the names as navigation hints, not a stable API. Do not compute “analysis overhead” by subtracting one row's width from another: several evaluator threads can overlap, and their spans are not a pair of enclosing phase timestamps. Use the main-thread phase markers and event categories to establish what was happening, then use the worker rows for detail.
The profile's Critical Path row is also a focused view rather than the whole
elapsed-time explanation. It shows actions on the observed critical path: a
timed chain whose dependencies gate completion in that capture. Loading,
analysis, garbage collection, fetching, queueing, and other overhead can still
contribute to wall time. 5.4.2 Critical Path develops how to compare that
chain with near-critical routes and choose an intervention.
Diagnose cold and warm analysis
A cold server must initialize state, load packages, and evaluate the requested graph. A warm server may reuse much of that work. The useful comparison is therefore not “does this trace contain package work?” but “how did the same workload's loading and analysis change between controlled cold and warm captures?”3
Look for these signals:
- A long
runAnalysisPhasespan on the main thread establishes that analysis deserves attention. On a warm incremental build, unexpectedly slow analysis can point to rule behavior such as repeatedly flattening depsets.1 - Numerous or expensive events in the
package creationcategory show package-loading work. Correlate them with package names and the loading portion of the invocation. They are not, by themselves, proof that Bazel downloaded external dependencies. The profile format usespackage creationas an event category.1 - Expensive
configuredTargetFunctionevents, when present in the producing Bazel version, focus the investigation on configured-target analysis. Compare the same targets and configuration before blaming target count or a particular rule. MerkleTree.buildis associated with constructing remote input Merkle trees, not with constructing Bazel's target dependency graph. It can become visible around remote cache or remote execution work, including recomputation after a remote cache miss when Bazel has discarded in-memory Merkle trees.2 Correlate it with remote setup, uploads, cache outcomes, and execution rows before deciding that analysis is the cause.
No single event proves that an instance was cold. Heavy package loading strongly supports that hypothesis only when the warm comparison removes it and the command, configuration, and repository state otherwise match. If both captures remain expensive, investigate the packages reached, macros and globs, configured targets, rule implementations, and memory pressure instead of merely restarting the server. Official profiling guidance calls out excessive targets, complex macros, recursive globs, and poor incremental rule behavior as possible loading or analysis costs.1
Trace: A warm incremental profile has a short runAnalysisPhase, but the build is still slow. Several skyframe evaluator execution-* rows contain long download outputs spans. Is analysis-cache reuse the leading hypothesis?
Reveal
No. The short analysis span weakens that hypothesis, while the execution rows point toward time spent materializing remote-cache results. Check cache and download metrics under the same remote-output policy. A cache hit avoids executing an action, but downloading its outputs still consumes time.3
Turn a trace into a testable hypothesis
Use a fixed sequence for each investigation:
- Locate the long top-level phase or idle interval.
- Identify the rows and event categories that occupy it.
- Correlate those events with labels, mnemonics, package names, cache activity, or remote-operation stages.
- State one causal hypothesis, such as “the representative edit causes broad configured-target reevaluation” or “remote input-tree construction dominates cache misses.”
- Change one relevant factor and capture several comparable profiles.
- Keep the diagnosis only if the phase, event population, and end-to-end time move as predicted.
The trace tells you where to ask the next question. It rarely tells you the repair alone. A long action may require action-graph inspection. Package-loading cost may require examining macros or globs. Remote setup and transfers may require cache and execution evidence. The disciplined move is to narrow the search surface, not to assign a cause from a lane name.
Read a timing profile from broad phase markers down to worker rows and event categories. Compare explicitly defined cold, warm no-op, and warm incremental workloads under matched conditions. package creation, configured-target evaluation, and remote Merkle-tree construction are clues to different kinds of work, not automatic diagnoses.
Trace row names and contents vary by Bazel version. Use them as navigation aids, corroborate them with phase timing and related evidence, and validate one causal hypothesis with repeated comparable captures.
Check your understanding · 3 questions
1.An engineer compares a cold-server profile with a warm incremental profile and sees much less package creation work in the warm run. Which conclusion is best supported?
Select one answer
2.A timing profile shows several skyframe evaluator lanes overlapping one another and an execution lane. Which reading practices are sound?
Select all that apply
3.A warm incremental build has a short runAnalysisPhase, but long download outputs spans occupy execution lanes. What should the engineer investigate next?
Select one answer
Footnotes
-
JSON Trace Profile — profile capture, version-dependent rows, phase markers, event categories, and common performance issues ↩1 ↩2 ↩3 ↩4 ↩5 ↩6
-
Command-Line Reference — current profile flags and remote Merkle-tree retention behavior ↩1 ↩2
-
Why is my Bazel build so slow? — cold-versus-warm server state, evaluator-row examples, and cache/download diagnosis ↩1 ↩2 ↩3 ↩4
-
Sponsored Session: Bazel Management for Developer Productivity Experts - Instructor: Billy Autrey — profile viewing and practical interpretation of execution, transfer, and critical-path spans ↩