Commit 247c4fff authored by Peter Marshall's avatar Peter Marshall Committed by Commit Bot

[tracing] Add the proto to json converter for trace events

This is used to convert files with binary proto data to the Trace Event
.json format for use with the chrome://tracing viewer.

Change-Id: Ib5478f6aa2326b5e085506859f4a7f30f95c79f5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1535823
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarMathias Bynens <mathias@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60422}
parent 48fcceea
proto-to-json.js
\ No newline at end of file
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=",
"dev": true
},
"@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
"dev": true
},
"@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
"dev": true
},
"@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=",
"dev": true
},
"@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
"dev": true,
"requires": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=",
"dev": true
},
"@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=",
"dev": true
},
"@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=",
"dev": true
},
"@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=",
"dev": true
},
"@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
"dev": true
},
"@types/long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz",
"integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==",
"dev": true
},
"@types/node": {
"version": "11.11.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.4.tgz",
"integrity": "sha512-02tIL+QIi/RW4E5xILdoAMjeJ9kYq5t5S2vciUdFPXv/ikFTb0zK8q9vXkg4+WAJuYXGiVT1H28AkD2C+IkXVw==",
"dev": true
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
"dev": true
},
"protobufjs": {
"version": "6.8.8",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
"integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
"dev": true,
"requires": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/long": "^4.0.0",
"@types/node": "^10.1.0",
"long": "^4.0.0"
},
"dependencies": {
"@types/node": {
"version": "10.14.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.1.tgz",
"integrity": "sha512-Rymt08vh1GaW4vYB6QP61/5m/CFLGnFZP++bJpWbiNxceNa6RBipDmb413jvtSf/R1gg5a/jQVl2jY4XVRscEA==",
"dev": true
}
}
},
"typescript": {
"version": "3.3.4000",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.4000.tgz",
"integrity": "sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==",
"dev": true
}
}
}
{
"private": true,
"scripts": {
"build": "tsc proto-to-json.ts"
},
"devDependencies": {
"@types/node": "^11.11.4",
"protobufjs": "^6.8.8",
"typescript": "^3.3.4000"
}
}
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as fs from 'fs';
import * as path from 'path';
import { Root } from 'protobufjs';
// Requirements: node 10.4.0+, npm
// Setup:
// (nvm is optional, you can also just install node manually)
// $ nvm use
// $ npm install
// $ npm run build
// Usage: node proto-to-json.js path_to_trace.proto input_file output_file
// Converts a binary proto file to a 'Trace Event Format' compatible .json file
// that can be used with chrome://tracing. Documentation of this format:
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
// Attempts to reproduce the logic of the JSONTraceWriter in V8 in terms of the
// JSON fields it will include/exclude based on the data present in the trace
// event.
// TODO(petermarshall): Replace with Array#flat once it lands in Node.js.
const flatten = <T>(a: T[], b: T[]) => { a.push(...b); return a; }
// Convert a string representing an int or uint (64 bit) to a Number or throw
// if the value won't fit.
function parseIntOrThrow(int: string) {
if (BigInt(int) > Number.MAX_SAFE_INTEGER) {
throw new Error("Loss of int precision");
}
return Number(int);
}
function uint64AsHexString(val : string) : string {
return "0x" + BigInt(val).toString(16);
}
function parseArgValue(arg: any) : any {
if (arg.jsonValue) {
return JSON.parse(arg.jsonValue);
}
if (typeof arg.stringValue !== 'undefined') {
return arg.stringValue;
}
if (typeof arg.uintValue !== 'undefined') {
return parseIntOrThrow(arg.uintValue);
}
if (typeof arg.intValue !== 'undefined') {
return parseIntOrThrow(arg.intValue);
}
if (typeof arg.boolValue !== 'undefined') {
return arg.boolValue;
}
if (typeof arg.doubleValue !== 'undefined') {
// Handle [-]Infinity and NaN which protobufjs outputs as strings here.
return typeof arg.doubleValue === 'string' ?
arg.doubleValue : Number(arg.doubleValue);
}
if (typeof arg.pointerValue !== 'undefined') {
return uint64AsHexString(arg.pointerValue);
}
}
// These come from
// https://cs.chromium.org/chromium/src/base/trace_event/common/trace_event_common.h
const TRACE_EVENT_FLAG_HAS_ID: number = 1 << 1;
const TRACE_EVENT_FLAG_FLOW_IN: number = 1 << 8;
const TRACE_EVENT_FLAG_FLOW_OUT: number = 1 << 9;
async function main() {
const root = new Root();
const { resolvePath } = root;
const numDirectoriesToStrip = 2;
let initialOrigin: string|null;
root.resolvePath = (origin, target) => {
if (!origin) {
initialOrigin = target;
for (let i = 0; i <= numDirectoriesToStrip; i++) {
initialOrigin = path.dirname(initialOrigin);
}
return resolvePath(origin, target);
}
return path.resolve(initialOrigin!, target);
};
const traceProto = await root.load(process.argv[2]);
const Trace = traceProto.lookupType("Trace");
const payload = await fs.promises.readFile(process.argv[3]);
const msg = Trace.decode(payload).toJSON();
const output = {
traceEvents: msg.packet
.filter((packet: any) => !!packet.chromeEvents)
.map((packet: any) => packet.chromeEvents.traceEvents)
.map((traceEvents: any) => traceEvents.map((e: any) => {
const bind_id = (e.flags & (TRACE_EVENT_FLAG_FLOW_IN |
TRACE_EVENT_FLAG_FLOW_OUT)) ? e.bindId : undefined;
const scope = (e.flags & TRACE_EVENT_FLAG_HAS_ID) && e.scope ?
e.scope : undefined;
return {
pid: e.processId,
tid: e.threadId,
ts: parseIntOrThrow(e.timestamp),
tts: parseIntOrThrow(e.threadTimestamp),
ph: String.fromCodePoint(e.phase),
cat: e.categoryGroupName,
name: e.name,
dur: parseIntOrThrow(e.duration),
tdur: parseIntOrThrow(e.threadDuration),
bind_id: bind_id,
flow_in: e.flags & TRACE_EVENT_FLAG_FLOW_IN ? true : undefined,
flow_out: e.flags & TRACE_EVENT_FLAG_FLOW_OUT ? true : undefined,
scope: scope,
id: (e.flags & TRACE_EVENT_FLAG_HAS_ID) ?
uint64AsHexString(e.id) : undefined,
args: (e.args || []).reduce((js_args: any, proto_arg: any) => {
js_args[proto_arg.name] = parseArgValue(proto_arg);
return js_args;
}, {})
};
}))
.reduce(flatten, [])
};
await fs.promises.writeFile(process.argv[4], JSON.stringify(output, null, 2));
}
main().catch(console.error);
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"lib": ["es6","dom"],
"outDir": "lib",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment