33 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44import fs from 'fs' ;
55import { Command , CommanderError , Option } from 'commander' ;
6+ import { parse as parseToml } from 'smol-toml' ;
67
78import {
89 serializeProfileToJsonSlabsFile ,
@@ -12,6 +13,7 @@ import {
1213import { computeCompactedProfile } from 'firefox-profiler/profile-logic/profile-compacting' ;
1314import { GOOGLE_STORAGE_BUCKET } from 'firefox-profiler/app-logic/constants' ;
1415import { compress } from 'firefox-profiler/utils/gz' ;
16+ import { insertStackLabels } from 'firefox-profiler/profile-logic/insert-stack-labels' ;
1517import { SymbolStore } from 'firefox-profiler/profile-logic/symbol-store' ;
1618import {
1719 symbolicateProfile ,
@@ -25,6 +27,11 @@ import {
2527} from 'firefox-profiler/profile-logic/wasm-symbolication' ;
2628import type { Profile } from 'firefox-profiler/types/profile' ;
2729import { assertExhaustiveCheck } from 'firefox-profiler/utils/types' ;
30+ import {
31+ type AutoLabel ,
32+ type LabelDescription ,
33+ resolveAllLabels ,
34+ } from 'firefox-profiler/utils/label-templates' ;
2835
2936/**
3037 * A CLI tool for editing profiles.
@@ -42,6 +49,9 @@ import { assertExhaustiveCheck } from 'firefox-profiler/utils/types';
4249 * node node-tools-dist/profiler-edit.js -i input.json.gz -o out.json.gz \
4350 * --symbolicate-wasm http://host/a.wasm=./a-unstripped.wasm \
4451 * --symbolicate-wasm http://host/b.wasm=./b-unstripped.wasm
52+ *
53+ * node node-tools-dist/profiler-edit.js --from-hash w1spyw917hg... -o out.json.gz \
54+ * --insert-label-frames known-functions.toml
4555 */
4656
4757type ProfileSource =
@@ -65,6 +75,7 @@ export interface CliOptions {
6575 output : string ;
6676 symbolicateWithServer ?: string ;
6777 symbolicateWasm : WasmSymbolicationCliSpec [ ] ;
78+ insertLabelFrames ?: string ;
6879}
6980
7081function loadWasmSymbolicationSpecs (
@@ -81,6 +92,42 @@ function loadWasmSymbolicationSpecs(
8192 } ) ;
8293}
8394
95+ /**
96+ * Reconstruct the func-name strings used by insertStackLabels' prefix matcher
97+ * (mirrors getLabelIndexForFunc in insert-stack-labels.ts), so auto-discovery
98+ * sees the same strings the labeler will compare against.
99+ */
100+ function collectFuncNames ( profile : Profile ) : string [ ] {
101+ const { funcTable, sources, stringArray } = profile . shared ;
102+ const result : string [ ] = [ ] ;
103+ for ( let i = 0 ; i < funcTable . length ; i ++ ) {
104+ let name = stringArray [ funcTable . name [ i ] ] ;
105+ const sourceIndex = funcTable . source [ i ] ;
106+ if ( sourceIndex !== null ) {
107+ const filename = stringArray [ sources . filename [ sourceIndex ] ] ;
108+ name += ` (${ filename } )` ;
109+ }
110+ result . push ( name ) ;
111+ }
112+ return result ;
113+ }
114+
115+ export type ParsedLabelToml = {
116+ labels : LabelDescription [ ] ;
117+ autoLabels : AutoLabel [ ] ;
118+ } ;
119+
120+ export function parseLabelToml ( tomlText : string ) : ParsedLabelToml {
121+ const data = parseToml ( tomlText ) as unknown as {
122+ labels ?: LabelDescription [ ] ;
123+ auto_labels ?: AutoLabel [ ] ;
124+ } ;
125+ return {
126+ labels : data . labels ?? [ ] ,
127+ autoLabels : data . auto_labels ?? [ ] ,
128+ } ;
129+ }
130+
84131async function loadProfile ( source : ProfileSource ) : Promise < Profile > {
85132 switch ( source . type ) {
86133 case 'FILE' : {
@@ -151,7 +198,7 @@ async function encodeProfileWithFilename(
151198}
152199
153200export async function run ( options : CliOptions ) {
154- const profile = await loadProfile ( options . input ) ;
201+ let profile = await loadProfile ( options . input ) ;
155202
156203 if ( options . symbolicateWithServer !== undefined ) {
157204 const server = options . symbolicateWithServer ;
@@ -205,6 +252,19 @@ export async function run(options: CliOptions) {
205252 loadWasmSymbolicationSpecs ( options . symbolicateWasm )
206253 ) ;
207254
255+ if ( options . insertLabelFrames !== undefined ) {
256+ console . log ( 'Inserting label frames...' ) ;
257+ const tomlText = fs . readFileSync ( options . insertLabelFrames , 'utf8' ) ;
258+ const parsed = parseLabelToml ( tomlText ) ;
259+ const funcNames = collectFuncNames ( profile ) ;
260+ const labels = resolveAllLabels (
261+ parsed . autoLabels ,
262+ parsed . labels ,
263+ funcNames
264+ ) ;
265+ profile = insertStackLabels ( profile , labels ) ;
266+ }
267+
208268 const { profile : compactedProfile } = computeCompactedProfile ( profile ) ;
209269
210270 const outputFilename = options . output ;
@@ -263,7 +323,8 @@ export function makeOptionsFromArgv(processArgv: string[]): CliOptions {
263323 )
264324 . argParser ( collectWasm )
265325 . default ( [ ] as WasmSymbolicationCliSpec [ ] )
266- ) ;
326+ )
327+ . option ( '--insert-label-frames <path>' , 'TOML file with label definitions' ) ;
267328
268329 program . parse ( processArgv ) ;
269330 const opts = program . opts ( ) ;
@@ -310,6 +371,11 @@ export function makeOptionsFromArgv(processArgv: string[]): CliOptions {
310371 ? opts . symbolicateWithServer
311372 : undefined ,
312373 symbolicateWasm : opts . symbolicateWasm ,
374+ insertLabelFrames :
375+ typeof opts . insertLabelFrames === 'string' &&
376+ opts . insertLabelFrames !== ''
377+ ? opts . insertLabelFrames
378+ : undefined ,
313379 } ;
314380}
315381
0 commit comments