roku-debug 0.22.4 → 0.22.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/LaunchConfiguration.d.ts +45 -0
- package/dist/PerfettoManager.d.ts +108 -0
- package/dist/PerfettoManager.js +394 -0
- package/dist/PerfettoManager.js.map +1 -0
- package/dist/RendezvousTracker.d.ts +1 -1
- package/dist/RokuECP.d.ts +27 -2
- package/dist/RokuECP.js +33 -0
- package/dist/RokuECP.js.map +1 -1
- package/dist/SceneGraphDebugCommandController.js.map +1 -1
- package/dist/adapters/customVariables/ifDateTime.js +1 -9
- package/dist/adapters/customVariables/ifDateTime.js.map +1 -1
- package/dist/bsc/threading/ThreadMessageHandler.d.ts +2 -2
- package/dist/debugProtocol/PluginInterface.d.ts +1 -1
- package/dist/debugProtocol/client/DebugProtocolClientPlugin.d.ts +2 -2
- package/dist/debugProtocol/events/ProtocolEvent.d.ts +3 -3
- package/dist/debugProtocol/server/DebugProtocolServerPlugin.d.ts +2 -2
- package/dist/debugSession/BrightScriptDebugSession.d.ts +14 -1
- package/dist/debugSession/BrightScriptDebugSession.js +144 -19
- package/dist/debugSession/BrightScriptDebugSession.js.map +1 -1
- package/dist/debugSession/Events.d.ts +53 -2
- package/dist/debugSession/Events.js +57 -1
- package/dist/debugSession/Events.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +4 -4
- package/dist/logging.d.ts +1 -1
- package/dist/managers/BreakpointManager.d.ts +2 -2
- package/dist/managers/ProjectManager.js +1 -1
- package/dist/managers/ProjectManager.js.map +1 -1
- package/dist/util.d.ts +6 -0
- package/dist/util.js +13 -0
- package/dist/util.js.map +1 -1
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## [0.22.6](https://github.com/rokucommunity/roku-debug/compare/0.22.5...v0.22.6) - 2026-03-09
|
|
10
|
+
### Added
|
|
11
|
+
- Perfetto tracing ([#286](https://github.com/rokucommunity/roku-debug/pull/286))
|
|
12
|
+
### Changed
|
|
13
|
+
- combined the `ifDateTime` `dateLocalized` and `timeLocalized` virtual variable into one time variable ([#295](https://github.com/rokucommunity/roku-debug/pull/295))
|
|
14
|
+
- Better messages when `limited` or `disabled` are detected for ecp-setting-mode ([#283](https://github.com/rokucommunity/roku-debug/pull/283))
|
|
15
|
+
- upgrade to [roku-deploy@3.16.2](https://github.com/rokucommunity/roku-deploy/blob/master/CHANGELOG.md#3162---2026-03-09). Notable changes since 3.15.0:
|
|
16
|
+
- Add `ecpSettingMode` to device-info interface ([#225](https://github.com/rokucommunity/roku-deploy/pull/225))
|
|
17
|
+
- Add support for detecting `ecpNetworkAccessMode` ([#223](https://github.com/rokucommunity/roku-deploy/pull/223))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [0.22.5](https://github.com/rokucommunity/roku-debug/compare/0.22.4...v0.22.5) - 2025-12-01
|
|
22
|
+
### Added
|
|
23
|
+
- Support packageTask, packagePath, packageUploadOverrides for complibs ([#282](https://github.com/rokucommunity/roku-debug/pull/282))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
9
27
|
## [0.22.4](https://github.com/rokucommunity/roku-debug/compare/0.22.3...v0.22.4) - 2025-11-19
|
|
10
28
|
### Added
|
|
11
29
|
- Add ability to install component libraries on device ([#279](https://github.com/rokucommunity/roku-debug/pull/279))
|
|
@@ -1085,3 +1103,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1085
1103
|
|
|
1086
1104
|
|
|
1087
1105
|
|
|
1106
|
+
|
|
1107
|
+
|
|
@@ -189,6 +189,29 @@ export interface LaunchConfiguration extends DebugProtocol.LaunchRequestArgument
|
|
|
189
189
|
* Will inject the OnDeviceComponent used by RDB for RALE like functionality inside of vscode.
|
|
190
190
|
*/
|
|
191
191
|
injectRdbOnDeviceComponent: boolean;
|
|
192
|
+
/**
|
|
193
|
+
* Configuration for profiling functionality
|
|
194
|
+
*/
|
|
195
|
+
profiling?: {
|
|
196
|
+
tracing?: {
|
|
197
|
+
/**
|
|
198
|
+
* Whether perfetto event profiling is enabled
|
|
199
|
+
*/
|
|
200
|
+
enable?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Directory where perfetto profile files should be stored
|
|
203
|
+
*/
|
|
204
|
+
dir?: string;
|
|
205
|
+
/**
|
|
206
|
+
* The name of the perfetto profile file. Can include variables like ${appTitle} and ${timestamp}
|
|
207
|
+
*/
|
|
208
|
+
filename?: string;
|
|
209
|
+
/**
|
|
210
|
+
* Whether to connect to perfetto on debug session start
|
|
211
|
+
*/
|
|
212
|
+
connectOnStart?: boolean;
|
|
213
|
+
};
|
|
214
|
+
};
|
|
192
215
|
/**
|
|
193
216
|
* Base path to the folder containing RDB files for OnDeviceComponent
|
|
194
217
|
*/
|
|
@@ -333,6 +356,28 @@ export interface ComponentLibraryConfiguration {
|
|
|
333
356
|
* Install component library on device if true
|
|
334
357
|
*/
|
|
335
358
|
install: boolean;
|
|
359
|
+
/**
|
|
360
|
+
* Task to run instead of roku-deploy to produce the .zip file that will be uploaded to the Roku.
|
|
361
|
+
*/
|
|
362
|
+
packageTask?: string;
|
|
363
|
+
/**
|
|
364
|
+
* Path to the .zip that will be uploaded to the Roku
|
|
365
|
+
*/
|
|
366
|
+
packagePath?: string;
|
|
367
|
+
/**
|
|
368
|
+
* Overrides for values used during the roku-deploy zip upload process, like the route and various form data. You probably don't need to change these..
|
|
369
|
+
*/
|
|
370
|
+
packageUploadOverrides?: {
|
|
371
|
+
/**
|
|
372
|
+
* The route to use for uploading to the Roku device. Defaults to 'plugin_install'
|
|
373
|
+
* @default 'plugin_install'
|
|
374
|
+
*/
|
|
375
|
+
route: string;
|
|
376
|
+
/**
|
|
377
|
+
* A dictionary of form fields to be included in the package upload request. Set a value to null to delete from the form
|
|
378
|
+
*/
|
|
379
|
+
formData: Record<string, any>;
|
|
380
|
+
};
|
|
336
381
|
/**
|
|
337
382
|
* The filename for the package.
|
|
338
383
|
*/
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ProfileType } from './debugSession/Events';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration interface for Perfetto tracing
|
|
4
|
+
*/
|
|
5
|
+
interface PerfettoConfig {
|
|
6
|
+
host: string;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
dir?: string;
|
|
9
|
+
filename?: string;
|
|
10
|
+
rootDir?: string;
|
|
11
|
+
remotePort?: number;
|
|
12
|
+
/** Channel ID to trace. Defaults to 'dev'. */
|
|
13
|
+
channelId?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class PerfettoManager {
|
|
16
|
+
constructor(config?: PerfettoConfig);
|
|
17
|
+
private config;
|
|
18
|
+
private socket;
|
|
19
|
+
private writeStream;
|
|
20
|
+
private pingTimer;
|
|
21
|
+
private emitter;
|
|
22
|
+
/**
|
|
23
|
+
* When tracing is active, this is the file we're currently writing to. Cleaned up whenever tracing stops or errors.
|
|
24
|
+
*/
|
|
25
|
+
private filePath?;
|
|
26
|
+
private logger;
|
|
27
|
+
/**
|
|
28
|
+
* Are we actively tracing right now
|
|
29
|
+
*/
|
|
30
|
+
private get isTracing();
|
|
31
|
+
/**
|
|
32
|
+
* Subscribe to PerfettoManager events
|
|
33
|
+
*/
|
|
34
|
+
on(eventName: 'enable', handler: (data: {
|
|
35
|
+
types: ProfileType[];
|
|
36
|
+
}) => void): () => void;
|
|
37
|
+
on(eventName: 'start', handler: (data: {
|
|
38
|
+
type: ProfileType;
|
|
39
|
+
}) => void): () => void;
|
|
40
|
+
on(eventName: 'stop', handler: (data: {
|
|
41
|
+
type: ProfileType;
|
|
42
|
+
result?: string;
|
|
43
|
+
}) => void): () => void;
|
|
44
|
+
on(eventName: 'error', handler: (data: {
|
|
45
|
+
type: ProfileType;
|
|
46
|
+
error: {
|
|
47
|
+
message: string;
|
|
48
|
+
stack?: string;
|
|
49
|
+
};
|
|
50
|
+
}) => void): () => void;
|
|
51
|
+
private emit;
|
|
52
|
+
/**
|
|
53
|
+
* Get app title from manifest file in cwd
|
|
54
|
+
*/
|
|
55
|
+
private getAppTitle;
|
|
56
|
+
private createWebSocket;
|
|
57
|
+
/**
|
|
58
|
+
* Start Perfetto tracing
|
|
59
|
+
* @param includeResultOnStop whether to include the file path when the 'stop' event fires. This should be false if the caller is going to emit their own 'stop' event (like when heapSnapshot is the activator of tracing.
|
|
60
|
+
*/
|
|
61
|
+
startTracing(options?: {
|
|
62
|
+
excludeResultOnStop: boolean;
|
|
63
|
+
}): Promise<void>;
|
|
64
|
+
private getFilename;
|
|
65
|
+
/**
|
|
66
|
+
* Get the next sequence number by scanning existing files in the directory
|
|
67
|
+
*/
|
|
68
|
+
private getNextSequenceNumber;
|
|
69
|
+
/**
|
|
70
|
+
* Stop Perfetto tracing gracefully.
|
|
71
|
+
*/
|
|
72
|
+
stopTracing(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Enable tracing on the Roku device. This returns true if we were successful, and throws if we we failed to enable
|
|
75
|
+
*/
|
|
76
|
+
enableTracing(): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Create a write stream for the given file path, wrapped in a promise to handle async stream readiness and errors.
|
|
79
|
+
* This ensures we don't start writing until the stream is ready, and that any errors are properly caught and emitted.
|
|
80
|
+
* @param filePath
|
|
81
|
+
* @returns
|
|
82
|
+
*/
|
|
83
|
+
private createWriteStream;
|
|
84
|
+
private startPingTimer;
|
|
85
|
+
/**
|
|
86
|
+
* Clean up all resources. Safe to call multiple times.
|
|
87
|
+
* When isCrash is true, destroys the write stream immediately instead of flushing it.
|
|
88
|
+
*/
|
|
89
|
+
private cleanup;
|
|
90
|
+
/**
|
|
91
|
+
* Get a file path if it exists, optionally skipping empty files.
|
|
92
|
+
*/
|
|
93
|
+
private getResult;
|
|
94
|
+
/**
|
|
95
|
+
* Dispose of all resources. Safe to call multiple times.
|
|
96
|
+
* Closes all sockets, file handles, and timers.
|
|
97
|
+
*/
|
|
98
|
+
dispose(): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Capture a heap graph snapshot. Can only be called when tracing is active and WebSocket is connected.
|
|
101
|
+
*/
|
|
102
|
+
captureHeapSnapshot(): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Helper to emit an error and also return it for throwing. This ensures all errors go through the same handling and are emitted to any listeners.
|
|
105
|
+
*/
|
|
106
|
+
private emitError;
|
|
107
|
+
}
|
|
108
|
+
export {};
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PerfettoManager = void 0;
|
|
4
|
+
/* eslint-disable no-template-curly-in-string */
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const fsExtra = require("fs-extra");
|
|
7
|
+
const ws_1 = require("ws");
|
|
8
|
+
const eventemitter3_1 = require("eventemitter3");
|
|
9
|
+
const brighterscript_1 = require("brighterscript");
|
|
10
|
+
const logging_1 = require("./logging");
|
|
11
|
+
const RokuECP_1 = require("./RokuECP");
|
|
12
|
+
const util_1 = require("./util");
|
|
13
|
+
class PerfettoManager {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
var _a, _b, _c;
|
|
16
|
+
var _d, _e, _f;
|
|
17
|
+
this.socket = null;
|
|
18
|
+
this.writeStream = null;
|
|
19
|
+
this.pingTimer = null;
|
|
20
|
+
this.emitter = new eventemitter3_1.EventEmitter();
|
|
21
|
+
this.logger = (0, logging_1.createLogger)('PerfettoManager');
|
|
22
|
+
this.config = config !== null && config !== void 0 ? config : {};
|
|
23
|
+
(_a = (_d = this.config).remotePort) !== null && _a !== void 0 ? _a : (_d.remotePort = 8060);
|
|
24
|
+
(_b = (_e = this.config).channelId) !== null && _b !== void 0 ? _b : (_e.channelId = 'dev');
|
|
25
|
+
// Set default traces directory if not provided
|
|
26
|
+
(_c = (_f = this.config).dir) !== null && _c !== void 0 ? _c : (_f.dir = (0, brighterscript_1.standardizePath) `${this.config.rootDir}/profiling`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Are we actively tracing right now
|
|
30
|
+
*/
|
|
31
|
+
get isTracing() {
|
|
32
|
+
//if we have a socket, we're tracing
|
|
33
|
+
return !!this.socket && this.socket.readyState === ws_1.WebSocket.OPEN;
|
|
34
|
+
}
|
|
35
|
+
on(eventName, handler) {
|
|
36
|
+
this.emitter.on(eventName, handler);
|
|
37
|
+
return () => {
|
|
38
|
+
if (this.emitter !== undefined) {
|
|
39
|
+
this.emitter.removeListener(eventName, handler);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
emit(eventName, data) {
|
|
44
|
+
this.emitter.emit(eventName, data);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get app title from manifest file in cwd
|
|
48
|
+
*/
|
|
49
|
+
getAppTitle(cwd) {
|
|
50
|
+
if (cwd) {
|
|
51
|
+
try {
|
|
52
|
+
const manifestPath = (0, brighterscript_1.standardizePath) `${cwd}/manifest`;
|
|
53
|
+
if (fs.existsSync(manifestPath)) {
|
|
54
|
+
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
55
|
+
const titleMatch = /^title=(.+)$/m.exec(manifestContent);
|
|
56
|
+
if (titleMatch && titleMatch[1]) {
|
|
57
|
+
return titleMatch[1].trim();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
this.logger.error('Error reading manifest file:', error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return 'trace';
|
|
66
|
+
}
|
|
67
|
+
createWebSocket() {
|
|
68
|
+
const url = `ws://${this.config.host}:${this.config.remotePort}/perfetto-session`;
|
|
69
|
+
this.socket = new ws_1.WebSocket(url);
|
|
70
|
+
return this.socket;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Start Perfetto tracing
|
|
74
|
+
* @param includeResultOnStop whether to include the file path when the 'stop' event fires. This should be false if the caller is going to emit their own 'stop' event (like when heapSnapshot is the activator of tracing.
|
|
75
|
+
*/
|
|
76
|
+
async startTracing(options) {
|
|
77
|
+
var _a;
|
|
78
|
+
if (!this.config.host) {
|
|
79
|
+
throw this.emitError(new Error('No host configured for Perfetto tracing'));
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
fsExtra.ensureDirSync(this.config.dir);
|
|
83
|
+
//async sanity check, if we're already tracing, don't start again
|
|
84
|
+
if (this.socket) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.createWebSocket();
|
|
88
|
+
this.filePath = (0, brighterscript_1.standardizePath) `${this.config.dir}/${this.getFilename()}`;
|
|
89
|
+
this.writeStream = await this.createWriteStream(this.filePath);
|
|
90
|
+
// Register all handlers before awaiting open
|
|
91
|
+
const connected = new Promise((resolve, reject) => {
|
|
92
|
+
const onConnectOpen = () => {
|
|
93
|
+
this.socket.off('error', onConnectError);
|
|
94
|
+
resolve();
|
|
95
|
+
};
|
|
96
|
+
const onConnectError = (error) => {
|
|
97
|
+
this.socket.off('open', onConnectOpen);
|
|
98
|
+
//remove our outer error handler since this is a connect error, not a runtime error, and we don't want to emit twice
|
|
99
|
+
this.socket.off('error', onError);
|
|
100
|
+
reject(error);
|
|
101
|
+
};
|
|
102
|
+
this.socket.once('open', onConnectOpen);
|
|
103
|
+
this.socket.once('error', onConnectError);
|
|
104
|
+
});
|
|
105
|
+
//register our general error handler next (so it'll be called second, and we can disconnect it if we get a connect error
|
|
106
|
+
const onError = (error) => {
|
|
107
|
+
var _a;
|
|
108
|
+
this.emitError(error);
|
|
109
|
+
// Force-close the socket so the 'close' handler fires cleanup + stop event
|
|
110
|
+
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.close();
|
|
111
|
+
};
|
|
112
|
+
this.socket.on('error', onError);
|
|
113
|
+
let backpressured = false;
|
|
114
|
+
this.socket.on('message', (data, isBinary) => {
|
|
115
|
+
var _a;
|
|
116
|
+
if (!isBinary || !this.writeStream) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Write the binary data to the file, handling backpressure by pausing the socket if the internal buffer is full
|
|
120
|
+
//only the FIRST message that causes backpressure should subscribe to the 'drain' event, to avoid multiple listeners
|
|
121
|
+
if (!this.writeStream.write(data) && !backpressured) {
|
|
122
|
+
backpressured = true;
|
|
123
|
+
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.pause();
|
|
124
|
+
this.writeStream.once('drain', () => {
|
|
125
|
+
var _a;
|
|
126
|
+
backpressured = false;
|
|
127
|
+
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.resume();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
this.socket.on('close', (code, reason) => {
|
|
132
|
+
this.logger.log(`Perfetto WebSocket closed. Code: ${code} Reason: ${reason.toString()}`);
|
|
133
|
+
const filePath = this.filePath;
|
|
134
|
+
this.cleanup().then(() => {
|
|
135
|
+
this.emit('stop', {
|
|
136
|
+
type: 'trace',
|
|
137
|
+
result: (options === null || options === void 0 ? void 0 : options.excludeResultOnStop)
|
|
138
|
+
? undefined
|
|
139
|
+
: this.getResult(filePath, { skipEmpty: true })
|
|
140
|
+
});
|
|
141
|
+
}).catch(e => this.logger.error(e));
|
|
142
|
+
});
|
|
143
|
+
await connected;
|
|
144
|
+
this.logger.log('Perfetto WebSocket connected:', this.socket.url);
|
|
145
|
+
this.emit('start', { type: 'trace' });
|
|
146
|
+
this.startPingTimer();
|
|
147
|
+
// we crashed, it's almost certainly due to a connection issue, we probably never started
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
throw this.emitError(new Error(`Error starting Perfetto tracing: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : String(error)}`));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
getFilename() {
|
|
154
|
+
var _a;
|
|
155
|
+
let filename = (_a = this.config.filename) !== null && _a !== void 0 ? _a : '${appTitle}_${timestamp}.perfetto-trace';
|
|
156
|
+
if (filename.includes('${timestamp}')) {
|
|
157
|
+
const timestamp = new Date()
|
|
158
|
+
.toLocaleString()
|
|
159
|
+
.replace(/[/:, ]/g, '-')
|
|
160
|
+
.replace(/-+/g, '-');
|
|
161
|
+
filename = filename.replaceAll('${timestamp}', timestamp);
|
|
162
|
+
// Remove sequence if the user has put timestamp
|
|
163
|
+
if (filename.includes('${sequence}')) {
|
|
164
|
+
filename = filename.replaceAll(/_?\$\{sequence\}_?/g, '');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const appTitle = this.getAppTitle(this.config.rootDir || '');
|
|
168
|
+
if (filename.includes('${appTitle}')) {
|
|
169
|
+
filename = filename.replace('${appTitle}', appTitle);
|
|
170
|
+
}
|
|
171
|
+
if (filename.includes('${sequence}')) {
|
|
172
|
+
const nextSequence = this.getNextSequenceNumber(filename, this.config.dir);
|
|
173
|
+
filename = filename.replace('${sequence}', String(nextSequence));
|
|
174
|
+
}
|
|
175
|
+
return filename;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get the next sequence number by scanning existing files in the directory
|
|
179
|
+
*/
|
|
180
|
+
getNextSequenceNumber(filenameTemplate, tracesDir) {
|
|
181
|
+
try {
|
|
182
|
+
if (!fs.existsSync(tracesDir)) {
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
const parts = filenameTemplate.split('${sequence}');
|
|
186
|
+
const prefix = parts[0] || '';
|
|
187
|
+
const suffix = parts[1] || '';
|
|
188
|
+
const files = fs.readdirSync(tracesDir);
|
|
189
|
+
let maxSequence = 0;
|
|
190
|
+
for (const file of files) {
|
|
191
|
+
if (file.startsWith(prefix) && file.endsWith(suffix)) {
|
|
192
|
+
const middle = file.slice(prefix.length, file.length - suffix.length);
|
|
193
|
+
const seq = parseInt(middle, 10);
|
|
194
|
+
if (!isNaN(seq) && seq > maxSequence) {
|
|
195
|
+
maxSequence = seq;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return maxSequence + 1;
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
this.logger.error('Error getting sequence number:', error);
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Stop Perfetto tracing gracefully.
|
|
208
|
+
*/
|
|
209
|
+
async stopTracing() {
|
|
210
|
+
if (!this.isTracing) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
await this.cleanup();
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Enable tracing on the Roku device. This returns true if we were successful, and throws if we we failed to enable
|
|
217
|
+
*/
|
|
218
|
+
async enableTracing() {
|
|
219
|
+
this.logger.log(`Enabling Perfetto tracing on channel ${this.config.channelId} at host ${this.config.host}`);
|
|
220
|
+
try {
|
|
221
|
+
const result = await RokuECP_1.rokuECP.enablePerfettoTracing({
|
|
222
|
+
host: this.config.host,
|
|
223
|
+
remotePort: this.config.remotePort,
|
|
224
|
+
channelId: this.config.channelId
|
|
225
|
+
});
|
|
226
|
+
//fail if our channel isn't in the list of enabled channels, even if the request was successful
|
|
227
|
+
const enabledChannels = result.enabledChannels.map(x => { var _a, _b; return (_b = (_a = x === null || x === void 0 ? void 0 : x.toString()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : ''; });
|
|
228
|
+
if (!enabledChannels.includes(this.config.channelId.toLowerCase())) {
|
|
229
|
+
throw new Error(`Failed to enable tracing. The request was successful but ${this.config.channelId} was not in the list of enabled channels: ${result.enabledChannels.join(', ')}`);
|
|
230
|
+
}
|
|
231
|
+
//emit that the following types of profiling are enabled available to start.
|
|
232
|
+
this.emit('enable', { types: ['trace', 'heapSnapshot'] });
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
throw this.emitError(Error(`Failed to enable tracing: ${error === null || error === void 0 ? void 0 : error.message}`));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Create a write stream for the given file path, wrapped in a promise to handle async stream readiness and errors.
|
|
241
|
+
* This ensures we don't start writing until the stream is ready, and that any errors are properly caught and emitted.
|
|
242
|
+
* @param filePath
|
|
243
|
+
* @returns
|
|
244
|
+
*/
|
|
245
|
+
async createWriteStream(filePath) {
|
|
246
|
+
const writeStream = await new Promise((resolve, reject) => {
|
|
247
|
+
const writeStream = fs.createWriteStream(filePath, { flags: 'w' });
|
|
248
|
+
const onReady = () => {
|
|
249
|
+
writeStream.off('error', onError);
|
|
250
|
+
resolve(writeStream);
|
|
251
|
+
};
|
|
252
|
+
const onError = (err) => {
|
|
253
|
+
writeStream.off('ready', onReady);
|
|
254
|
+
this.logger.error('File write error:', err);
|
|
255
|
+
reject(err);
|
|
256
|
+
};
|
|
257
|
+
writeStream.once('ready', onReady);
|
|
258
|
+
writeStream.once('error', onError);
|
|
259
|
+
});
|
|
260
|
+
return writeStream;
|
|
261
|
+
}
|
|
262
|
+
startPingTimer() {
|
|
263
|
+
//skip if we're already pinging
|
|
264
|
+
if (this.pingTimer) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
this.pingTimer = setInterval(() => {
|
|
268
|
+
var _a;
|
|
269
|
+
if (((_a = this.socket) === null || _a === void 0 ? void 0 : _a.readyState) === ws_1.WebSocket.OPEN) {
|
|
270
|
+
try {
|
|
271
|
+
this.socket.ping();
|
|
272
|
+
}
|
|
273
|
+
catch (e) {
|
|
274
|
+
this.logger.error('Ping error:', e);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}, 30000);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Clean up all resources. Safe to call multiple times.
|
|
281
|
+
* When isCrash is true, destroys the write stream immediately instead of flushing it.
|
|
282
|
+
*/
|
|
283
|
+
async cleanup() {
|
|
284
|
+
if (this.pingTimer) {
|
|
285
|
+
clearInterval(this.pingTimer);
|
|
286
|
+
this.pingTimer = null;
|
|
287
|
+
}
|
|
288
|
+
if (this.socket) {
|
|
289
|
+
const socket = this.socket;
|
|
290
|
+
this.socket = null;
|
|
291
|
+
if (socket.readyState !== ws_1.WebSocket.CLOSED) {
|
|
292
|
+
await new Promise(resolve => {
|
|
293
|
+
socket.once('close', resolve);
|
|
294
|
+
socket.close();
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
socket.removeAllListeners();
|
|
298
|
+
}
|
|
299
|
+
if (this.writeStream) {
|
|
300
|
+
const writeStream = this.writeStream;
|
|
301
|
+
this.writeStream = null;
|
|
302
|
+
await new Promise(resolve => {
|
|
303
|
+
writeStream.end(() => resolve());
|
|
304
|
+
});
|
|
305
|
+
writeStream.removeAllListeners();
|
|
306
|
+
}
|
|
307
|
+
this.filePath = undefined;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get a file path if it exists, optionally skipping empty files.
|
|
311
|
+
*/
|
|
312
|
+
getResult(filePath, options) {
|
|
313
|
+
if (!filePath) {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
const size = fs.statSync(filePath).size;
|
|
318
|
+
if ((options === null || options === void 0 ? void 0 : options.skipEmpty) && size === 0) {
|
|
319
|
+
return undefined;
|
|
320
|
+
}
|
|
321
|
+
return filePath;
|
|
322
|
+
}
|
|
323
|
+
catch (_a) {
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Dispose of all resources. Safe to call multiple times.
|
|
329
|
+
* Closes all sockets, file handles, and timers.
|
|
330
|
+
*/
|
|
331
|
+
async dispose() {
|
|
332
|
+
await this.cleanup();
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Capture a heap graph snapshot. Can only be called when tracing is active and WebSocket is connected.
|
|
336
|
+
*/
|
|
337
|
+
async captureHeapSnapshot() {
|
|
338
|
+
let thisFunctionStartedTracing = false;
|
|
339
|
+
try {
|
|
340
|
+
if (!this.isTracing) {
|
|
341
|
+
thisFunctionStartedTracing = true;
|
|
342
|
+
//start tracing, and exclude the result on stop
|
|
343
|
+
await this.startTracing({
|
|
344
|
+
excludeResultOnStop: true
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
let filePath = this.filePath;
|
|
348
|
+
this.emit('start', {
|
|
349
|
+
type: 'heapSnapshot'
|
|
350
|
+
});
|
|
351
|
+
await RokuECP_1.rokuECP.captureHeapSnapshot({
|
|
352
|
+
channelId: this.config.channelId,
|
|
353
|
+
host: this.config.host,
|
|
354
|
+
remotePort: this.config.remotePort
|
|
355
|
+
});
|
|
356
|
+
await util_1.util.sleep(1000);
|
|
357
|
+
this.emit('stop', {
|
|
358
|
+
type: 'heapSnapshot',
|
|
359
|
+
result: thisFunctionStartedTracing
|
|
360
|
+
? this.getResult(filePath, { skipEmpty: true })
|
|
361
|
+
: undefined
|
|
362
|
+
});
|
|
363
|
+
if (thisFunctionStartedTracing) {
|
|
364
|
+
await this.stopTracing();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
//regardless of success or failure, we want to emit that the snapshot process is no longer active so the UI can update accordingly
|
|
369
|
+
this.emit('stop', {
|
|
370
|
+
type: 'heapSnapshot',
|
|
371
|
+
result: undefined
|
|
372
|
+
});
|
|
373
|
+
if (thisFunctionStartedTracing) {
|
|
374
|
+
await this.stopTracing();
|
|
375
|
+
}
|
|
376
|
+
throw this.emitError(new Error(`Failed to capture snapshot: ${String(error)}`));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Helper to emit an error and also return it for throwing. This ensures all errors go through the same handling and are emitted to any listeners.
|
|
381
|
+
*/
|
|
382
|
+
emitError(error) {
|
|
383
|
+
this.logger.error(error);
|
|
384
|
+
this.emit('error', {
|
|
385
|
+
error: {
|
|
386
|
+
message: error.message,
|
|
387
|
+
stack: error.stack
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
return error;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
exports.PerfettoManager = PerfettoManager;
|
|
394
|
+
//# sourceMappingURL=PerfettoManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PerfettoManager.js","sourceRoot":"","sources":["../src/PerfettoManager.ts"],"names":[],"mappings":"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"}
|
package/dist/RokuECP.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import type * as requestType from 'request';
|
|
2
2
|
export declare class RokuECP {
|
|
3
3
|
private doRequest;
|
|
4
|
+
/**
|
|
5
|
+
* Enables perfetto tracing for the specified channel
|
|
6
|
+
* @param channelID
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
enablePerfettoTracing(options: BaseOptions & {
|
|
10
|
+
channelId: string;
|
|
11
|
+
}): Promise<EcpPerfettoEnableData>;
|
|
12
|
+
/**
|
|
13
|
+
* capture a heap snapshot. This doesn't return it, but instead will cause it to be written to the already-connected perfetto websocket.
|
|
14
|
+
* @param channelId
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
captureHeapSnapshot(options: BaseOptions & {
|
|
18
|
+
channelId: string;
|
|
19
|
+
}): Promise<EcpHeapSnapshotData>;
|
|
4
20
|
private getEcpStatus;
|
|
5
21
|
private parseResponse;
|
|
6
22
|
getRegistry(options: BaseOptions & {
|
|
@@ -29,8 +45,8 @@ interface BaseEcpResponse {
|
|
|
29
45
|
status: EcpStatus;
|
|
30
46
|
errorMessage?: string;
|
|
31
47
|
}
|
|
32
|
-
export
|
|
33
|
-
export
|
|
48
|
+
export type RokuEcpParam<T extends keyof RokuECP> = Parameters<RokuECP[T]>[0];
|
|
49
|
+
export type ParsedEcpRoot<T1 extends string = string, T2 extends ParsedEcpBase = ParsedEcpBase> = {
|
|
34
50
|
[key in T1]: T2;
|
|
35
51
|
};
|
|
36
52
|
interface ParsedEcpBase {
|
|
@@ -59,9 +75,18 @@ export interface EcpAppStateData {
|
|
|
59
75
|
status: EcpStatus;
|
|
60
76
|
errorMessage?: string;
|
|
61
77
|
}
|
|
78
|
+
export interface EcpPerfettoEnableData extends BaseEcpResponse {
|
|
79
|
+
enabledChannels: string[];
|
|
80
|
+
timestamp?: number;
|
|
81
|
+
timestampEnd?: number;
|
|
82
|
+
}
|
|
62
83
|
export interface EcpExitAppData {
|
|
63
84
|
status: EcpStatus;
|
|
64
85
|
errorMessage?: string;
|
|
65
86
|
}
|
|
87
|
+
export interface EcpHeapSnapshotData extends BaseEcpResponse {
|
|
88
|
+
timestamp: number;
|
|
89
|
+
timestampEnd: number;
|
|
90
|
+
}
|
|
66
91
|
export declare const rokuECP: RokuECP;
|
|
67
92
|
export {};
|