11cb0ef41Sopenharmony_ci// Copyright 2019 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciimport * as fs from 'fs';
61cb0ef41Sopenharmony_ciimport * as path from 'path';
71cb0ef41Sopenharmony_ciimport { Root } from 'protobufjs';
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci// Requirements: node 10.4.0+, npm
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ci// Setup:
121cb0ef41Sopenharmony_ci// (nvm is optional, you can also just install node manually)
131cb0ef41Sopenharmony_ci// $ nvm use
141cb0ef41Sopenharmony_ci// $ npm install
151cb0ef41Sopenharmony_ci// $ npm run build
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci// Usage: node proto-to-json.js path_to_trace.proto input_file output_file
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci// Converts a binary proto file to a 'Trace Event Format' compatible .json file
201cb0ef41Sopenharmony_ci// that can be used with chrome://tracing. Documentation of this format:
211cb0ef41Sopenharmony_ci// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci// Attempts to reproduce the logic of the JSONTraceWriter in V8 in terms of the
241cb0ef41Sopenharmony_ci// JSON fields it will include/exclude based on the data present in the trace
251cb0ef41Sopenharmony_ci// event.
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ci// Convert a string representing an int or uint (64 bit) to a Number or throw
281cb0ef41Sopenharmony_ci// if the value won't fit.
291cb0ef41Sopenharmony_cifunction parseIntOrThrow(int: string) {
301cb0ef41Sopenharmony_ci  if (BigInt(int) > Number.MAX_SAFE_INTEGER) {
311cb0ef41Sopenharmony_ci    throw new Error("Loss of int precision");
321cb0ef41Sopenharmony_ci  }
331cb0ef41Sopenharmony_ci  return Number(int);
341cb0ef41Sopenharmony_ci}
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_cifunction uint64AsHexString(val : string) : string {
371cb0ef41Sopenharmony_ci  return "0x" + BigInt(val).toString(16);
381cb0ef41Sopenharmony_ci}
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_cifunction parseArgValue(arg: any) : any {
411cb0ef41Sopenharmony_ci  if (arg.jsonValue) {
421cb0ef41Sopenharmony_ci    return JSON.parse(arg.jsonValue);
431cb0ef41Sopenharmony_ci  }
441cb0ef41Sopenharmony_ci  if (typeof arg.stringValue !== 'undefined') {
451cb0ef41Sopenharmony_ci    return arg.stringValue;
461cb0ef41Sopenharmony_ci  }
471cb0ef41Sopenharmony_ci  if (typeof arg.uintValue !== 'undefined') {
481cb0ef41Sopenharmony_ci    return parseIntOrThrow(arg.uintValue);
491cb0ef41Sopenharmony_ci  }
501cb0ef41Sopenharmony_ci  if (typeof arg.intValue !== 'undefined') {
511cb0ef41Sopenharmony_ci    return parseIntOrThrow(arg.intValue);
521cb0ef41Sopenharmony_ci  }
531cb0ef41Sopenharmony_ci  if (typeof arg.boolValue !== 'undefined') {
541cb0ef41Sopenharmony_ci    return arg.boolValue;
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci  if (typeof arg.doubleValue !== 'undefined') {
571cb0ef41Sopenharmony_ci    // Handle [-]Infinity and NaN which protobufjs outputs as strings here.
581cb0ef41Sopenharmony_ci    return typeof arg.doubleValue === 'string' ?
591cb0ef41Sopenharmony_ci        arg.doubleValue : Number(arg.doubleValue);
601cb0ef41Sopenharmony_ci  }
611cb0ef41Sopenharmony_ci  if (typeof arg.pointerValue !== 'undefined') {
621cb0ef41Sopenharmony_ci    return uint64AsHexString(arg.pointerValue);
631cb0ef41Sopenharmony_ci  }
641cb0ef41Sopenharmony_ci}
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci// These come from
671cb0ef41Sopenharmony_ci// https://cs.chromium.org/chromium/src/base/trace_event/common/trace_event_common.h
681cb0ef41Sopenharmony_ciconst TRACE_EVENT_FLAG_HAS_ID: number = 1 << 1;
691cb0ef41Sopenharmony_ciconst TRACE_EVENT_FLAG_FLOW_IN: number = 1 << 8;
701cb0ef41Sopenharmony_ciconst TRACE_EVENT_FLAG_FLOW_OUT: number = 1 << 9;
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ciasync function main() {
731cb0ef41Sopenharmony_ci  const root = new Root();
741cb0ef41Sopenharmony_ci  const { resolvePath } = root;
751cb0ef41Sopenharmony_ci  const numDirectoriesToStrip = 2;
761cb0ef41Sopenharmony_ci  let initialOrigin: string|null;
771cb0ef41Sopenharmony_ci  root.resolvePath = (origin, target) => {
781cb0ef41Sopenharmony_ci    if (!origin) {
791cb0ef41Sopenharmony_ci      initialOrigin = target;
801cb0ef41Sopenharmony_ci      for (let i = 0; i <= numDirectoriesToStrip; i++) {
811cb0ef41Sopenharmony_ci        initialOrigin = path.dirname(initialOrigin);
821cb0ef41Sopenharmony_ci      }
831cb0ef41Sopenharmony_ci      return resolvePath(origin, target);
841cb0ef41Sopenharmony_ci    }
851cb0ef41Sopenharmony_ci    return path.resolve(initialOrigin!, target);
861cb0ef41Sopenharmony_ci  };
871cb0ef41Sopenharmony_ci  const traceProto = await root.load(process.argv[2]);
881cb0ef41Sopenharmony_ci  const Trace = traceProto.lookupType("Trace");
891cb0ef41Sopenharmony_ci  const payload = await fs.promises.readFile(process.argv[3]);
901cb0ef41Sopenharmony_ci  const msg = Trace.decode(payload).toJSON();
911cb0ef41Sopenharmony_ci  const output = {
921cb0ef41Sopenharmony_ci    traceEvents: msg.packet
931cb0ef41Sopenharmony_ci      .filter((packet: any) => !!packet.chromeEvents)
941cb0ef41Sopenharmony_ci      .map((packet: any) => packet.chromeEvents.traceEvents)
951cb0ef41Sopenharmony_ci      .map((traceEvents: any) => traceEvents.map((e: any) => {
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci        const bind_id = (e.flags & (TRACE_EVENT_FLAG_FLOW_IN |
981cb0ef41Sopenharmony_ci          TRACE_EVENT_FLAG_FLOW_OUT)) ? e.bindId : undefined;
991cb0ef41Sopenharmony_ci        const scope = (e.flags & TRACE_EVENT_FLAG_HAS_ID) && e.scope ?
1001cb0ef41Sopenharmony_ci            e.scope : undefined;
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci        return {
1031cb0ef41Sopenharmony_ci          pid: e.processId,
1041cb0ef41Sopenharmony_ci          tid: e.threadId,
1051cb0ef41Sopenharmony_ci          ts: parseIntOrThrow(e.timestamp),
1061cb0ef41Sopenharmony_ci          tts: parseIntOrThrow(e.threadTimestamp),
1071cb0ef41Sopenharmony_ci          ph: String.fromCodePoint(e.phase),
1081cb0ef41Sopenharmony_ci          cat: e.categoryGroupName,
1091cb0ef41Sopenharmony_ci          name: e.name,
1101cb0ef41Sopenharmony_ci          dur: parseIntOrThrow(e.duration),
1111cb0ef41Sopenharmony_ci          tdur: parseIntOrThrow(e.threadDuration),
1121cb0ef41Sopenharmony_ci          bind_id: bind_id,
1131cb0ef41Sopenharmony_ci          flow_in: e.flags & TRACE_EVENT_FLAG_FLOW_IN ? true : undefined,
1141cb0ef41Sopenharmony_ci          flow_out: e.flags & TRACE_EVENT_FLAG_FLOW_OUT ? true : undefined,
1151cb0ef41Sopenharmony_ci          scope: scope,
1161cb0ef41Sopenharmony_ci          id: (e.flags & TRACE_EVENT_FLAG_HAS_ID) ?
1171cb0ef41Sopenharmony_ci              uint64AsHexString(e.id) : undefined,
1181cb0ef41Sopenharmony_ci          args: (e.args || []).reduce((js_args: any, proto_arg: any) => {
1191cb0ef41Sopenharmony_ci            js_args[proto_arg.name] = parseArgValue(proto_arg);
1201cb0ef41Sopenharmony_ci            return js_args;
1211cb0ef41Sopenharmony_ci          }, {})
1221cb0ef41Sopenharmony_ci        };
1231cb0ef41Sopenharmony_ci      }))
1241cb0ef41Sopenharmony_ci      .flat()
1251cb0ef41Sopenharmony_ci  };
1261cb0ef41Sopenharmony_ci  await fs.promises.writeFile(process.argv[4], JSON.stringify(output, null, 2));
1271cb0ef41Sopenharmony_ci}
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_cimain().catch(console.error);
130