Skip to content

Commit d3396e8

Browse files
committed
feat(profiling): generate chrome compatible timeline data
1 parent 35f9fd2 commit d3396e8

File tree

4 files changed

+136
-13
lines changed

4 files changed

+136
-13
lines changed

lib/bootstrap.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ injector.require(
154154
"./services/android-device-debug-service"
155155
);
156156

157+
injector.require(
158+
"timelineProfilerService",
159+
"./services/timeline-profiler-service"
160+
);
157161
injector.require("userSettingsService", "./services/user-settings-service");
158162
injector.requirePublic(
159163
"analyticsSettingsService",

lib/common/mobile/device-log-provider.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import * as chalk from "chalk";
66
import { LoggerConfigData } from "../../constants";
77
import { IOptions } from "../../declarations";
88

9+
import { ITimelineProfilerService } from "../../services/timeline-profiler-service";
10+
911
export class DeviceLogProvider extends DeviceLogProviderBase {
1012
constructor(
1113
protected $logFilter: Mobile.ILogFilter,
1214
protected $logger: ILogger,
1315
protected $logSourceMapService: Mobile.ILogSourceMapService,
16+
protected $timelineProfilerService: ITimelineProfilerService,
1417
protected $options: IOptions
1518
) {
1619
super($logFilter, $logger, $logSourceMapService);
@@ -29,7 +32,9 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
2932
data,
3033
loggingOptions
3134
);
35+
3236
if (data) {
37+
this.$timelineProfilerService.processLogData(data, deviceIdentifier);
3338
this.logDataCore(data, deviceIdentifier);
3439
this.emit(DEVICE_LOG_EVENT_NAME, lineText, deviceIdentifier, platform);
3540
}

lib/nativescript-cli.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ installUncaughtExceptionListener(
2222
);
2323

2424
const logger: ILogger = injector.resolve("logger");
25-
const originalProcessOn = process.on;
25+
// const originalProcessOn = process.on;
2626

27-
process.on = (event: string, listener: any): any => {
28-
if (event === "SIGINT") {
29-
logger.trace(
30-
`Trying to handle SIGINT event. CLI overrides this behavior and does not allow handling SIGINT as this causes issues with Ctrl + C in terminal.`
31-
);
32-
const msg = "The stackTrace of the location trying to handle SIGINT is:";
33-
const stackTrace = new Error(msg).stack || "";
34-
logger.trace(stackTrace.replace(`Error: ${msg}`, msg));
35-
} else {
36-
return originalProcessOn.apply(process, [event, listener]);
37-
}
38-
};
27+
// process.on = (event: string, listener: any): any => {
28+
// if (event === "SIGINT") {
29+
// logger.trace(
30+
// `Trying to handle SIGINT event. CLI overrides this behavior and does not allow handling SIGINT as this causes issues with Ctrl + C in terminal.`
31+
// );
32+
// const msg = "The stackTrace of the location trying to handle SIGINT is:";
33+
// const stackTrace = new Error(msg).stack || "";
34+
// logger.trace(stackTrace.replace(`Error: ${msg}`, msg));
35+
// } else {
36+
// return originalProcessOn.apply(process, [event, listener]);
37+
// }
38+
// };
3939

4040
/* tslint:disable:no-floating-promises */
4141
(async () => {
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { IFileSystem } from "../common/declarations";
2+
import { cache } from "../common/decorators";
3+
import { injector } from "../common/yok";
4+
import { IProjectConfigService } from "../definitions/project";
5+
import * as path from "path";
6+
7+
export interface ITimelineProfilerService {
8+
processLogData(data: string, deviceIdentifier: string): void;
9+
}
10+
11+
const TIMELINE_LOG_RE = /Timeline:\s*(\d*.?\d*ms:\s*)?([^\:]*\:)?(.*)\((\d*.?\d*)ms\.?\s*-\s*(\d*.\d*)ms\.?\)/;
12+
13+
enum ChromeTraceEventPhase {
14+
BEGIN = "B",
15+
END = "E",
16+
INSTANT = "i",
17+
COMPLETE = "X",
18+
}
19+
20+
interface ChromeTraceEvent {
21+
ts: number;
22+
pid: number;
23+
tid: number;
24+
/** event phase */
25+
ph?: ChromeTraceEventPhase | string;
26+
[otherData: string]: any;
27+
}
28+
29+
interface DeviceTimeline {
30+
startPoint: number;
31+
timeline: ChromeTraceEvent[];
32+
}
33+
34+
export class TimelineProfilerService implements ITimelineProfilerService {
35+
private timelines: Map<string, DeviceTimeline>;
36+
constructor(
37+
private $projectConfigService: IProjectConfigService,
38+
private $fs: IFileSystem,
39+
private $logger: ILogger
40+
) {
41+
this.timelines = new Map();
42+
43+
process.on("exit", this.writeTimelines.bind(this));
44+
process.on("SIGINT", this.writeTimelines.bind(this));
45+
}
46+
47+
public processLogData(data: string, deviceIdentifier: string) {
48+
if (!this.isEnabled()) {
49+
return;
50+
}
51+
52+
if (!this.timelines.has(deviceIdentifier)) {
53+
this.timelines.set(deviceIdentifier, {
54+
startPoint: null,
55+
timeline: [],
56+
});
57+
}
58+
59+
const deviceTimeline = this.timelines.get(deviceIdentifier);
60+
61+
data.split("\n").forEach((line) => {
62+
const trace = this.toTrace(line.trim());
63+
if (trace) {
64+
deviceTimeline.startPoint ??= trace.from;
65+
deviceTimeline.timeline.push(trace);
66+
}
67+
});
68+
}
69+
70+
@cache()
71+
private isEnabled() {
72+
return this.$projectConfigService.getValue("profiling") === "timeline";
73+
}
74+
75+
private toTrace(text: string): ChromeTraceEvent | undefined {
76+
const result = text.match(TIMELINE_LOG_RE);
77+
if (!result) {
78+
return;
79+
}
80+
81+
const trace = {
82+
domain: result[2]?.trim().replace(":", ""),
83+
name: result[3].trim(),
84+
from: parseFloat(result[4]),
85+
to: parseFloat(result[5]),
86+
};
87+
88+
return {
89+
pid: 1,
90+
tid: 1,
91+
ts: trace.from * 1000,
92+
dur: (trace.to - trace.from) * 1000,
93+
name: trace.name,
94+
cat: trace.domain ?? "default",
95+
ph: ChromeTraceEventPhase.COMPLETE,
96+
};
97+
}
98+
99+
private writeTimelines() {
100+
if (this.isEnabled()) {
101+
this.$logger.info("Writing timeline data to json...");
102+
this.timelines.forEach((deviceTimeline, deviceIdentifier) => {
103+
this.$fs.writeJson(
104+
path.resolve(process.cwd(), `timeline-${deviceIdentifier}.json`),
105+
deviceTimeline.timeline
106+
);
107+
});
108+
}
109+
110+
process.exit();
111+
}
112+
}
113+
114+
injector.register("timelineProfilerService", TimelineProfilerService);

0 commit comments

Comments
 (0)