diff --git a/tools/winscope-ng/src/trace_collection/web_adb/winscope_proxy.py b/tools/winscope-ng/src/adb/winscope_proxy.py similarity index 100% rename from tools/winscope-ng/src/trace_collection/web_adb/winscope_proxy.py rename to tools/winscope-ng/src/adb/winscope_proxy.py diff --git a/tools/winscope-ng/src/trace_collection/adb_proxy.component.spec.ts b/tools/winscope-ng/src/app/adb_proxy.component.spec.ts similarity index 100% rename from tools/winscope-ng/src/trace_collection/adb_proxy.component.spec.ts rename to tools/winscope-ng/src/app/adb_proxy.component.spec.ts diff --git a/tools/winscope-ng/src/trace_collection/adb_proxy.component.ts b/tools/winscope-ng/src/app/adb_proxy.component.ts similarity index 90% rename from tools/winscope-ng/src/trace_collection/adb_proxy.component.ts rename to tools/winscope-ng/src/app/adb_proxy.component.ts index c40de9821..c04923c22 100644 --- a/tools/winscope-ng/src/trace_collection/adb_proxy.component.ts +++ b/tools/winscope-ng/src/app/adb_proxy.component.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { Component, Input, Output, EventEmitter } from "@angular/core"; -import { ProxyClient, ProxyState } from "./proxy_client"; +import { ProxyClient, ProxyState } from "../trace_collection/proxy_client"; @Component({ selector: "adb-proxy", @@ -29,7 +29,7 @@ import { ProxyClient, ProxyState } from "./proxy_client";

Python 3.5+ and ADB are required.

Run:

python3
-
$ANDROID_BUILD_TOP/development/tools/winscope-ng/adb/winscope_proxy.py
+
$ANDROID_BUILD_TOP/development/tools/winscope-ng/src/adb/winscope_proxy.py

Or get it from the AOSP repository.

@@ -42,14 +42,14 @@ import { ProxyClient, ProxyState } from "./proxy_client";
- update + update Your local proxy version is incompatible with Winscope.

Please update the proxy to version {{ proxyVersion }}.

Run:

python3
-
$ANDROID_BUILD_TOP/development/tools/winscope-ng/adb/winscope_proxy.py
+
$ANDROID_BUILD_TOP/development/tools/winscope-ng/src/adb/winscope_proxy.py

Or get it from the AOSP repository.

@@ -62,7 +62,7 @@ import { ProxyClient, ProxyState } from "./proxy_client";
- lock + lock Proxy authorisation required
@@ -78,23 +78,20 @@ import { ProxyClient, ProxyState } from "./proxy_client";
`, - styles: [".proxy-key-field {width: 30rem}", ".icon-message {vertical-align: middle;}"] + styles: [".proxy-key-field {width: 30rem}"] }) export class AdbProxyComponent { - states = ProxyState; - - proxyKeyItem = ""; - @Input() - proxy: any = {}; - - readonly proxyVersion = this.proxy.VERSION; + proxy: any = {}; @Output() - proxyChange = new EventEmitter(); + proxyChange = new EventEmitter(); @Output() addKey = new EventEmitter(); + states = ProxyState; + proxyKeyItem = ""; + readonly proxyVersion = this.proxy.VERSION; readonly downloadProxyUrl: string = "https://android.googlesource.com/platform/development/+/master/tools/winscope-ng/adb/winscope_proxy.py"; public restart() { @@ -102,4 +99,4 @@ export class AdbProxyComponent { this.proxy.setState(this.states.CONNECTING); this.proxyChange.emit(this.proxy); } -} \ No newline at end of file +} diff --git a/tools/winscope-ng/src/app/app.component.ts b/tools/winscope-ng/src/app/app.component.ts index febcb1d82..e159f08c9 100644 --- a/tools/winscope-ng/src/app/app.component.ts +++ b/tools/winscope-ng/src/app/app.component.ts @@ -26,9 +26,9 @@ import { PersistentStore } from "../common/persistent_store";
Winscope Viewer 2.0
-
+
- + Upload Traces @@ -38,10 +38,11 @@ import { PersistentStore } from "../common/persistent_store";
-
- - - +
+ + Loaded data + +
@@ -59,9 +60,10 @@ import { PersistentStore } from "../common/persistent_store"; export class AppComponent { title = "winscope-ng"; - private core!: Core; + core: Core = new Core(); states = ProxyState; store: PersistentStore = new PersistentStore(); + dataLoaded: boolean = false; constructor( @Inject(Injector) injector: Injector @@ -70,10 +72,16 @@ export class AppComponent { createCustomElement(ViewerWindowManagerComponent, {injector})); } - public async onInputFile(event: Event) { - const files = await this.getInputFiles(event); + onCoreChange(newCore: Core) { + this.core = newCore; + } - this.core = new Core(); + onDataLoadedChange(loaded: boolean) { + this.dataLoaded = loaded; + } + + public async onInputFile(event: Event) { + const files = this.getInputFiles(event); await this.core.bootstrap(files); const viewersDiv = document.querySelector("div#viewers")!; @@ -99,4 +107,9 @@ export class AppComponent { return [files[0]]; } + + public clearData() { + this.dataLoaded = false; + this.core.clearData(); + } } diff --git a/tools/winscope-ng/src/app/app.module.ts b/tools/winscope-ng/src/app/app.module.ts index 444abe182..0cb216f58 100644 --- a/tools/winscope-ng/src/app/app.module.ts +++ b/tools/winscope-ng/src/app/app.module.ts @@ -18,10 +18,10 @@ import { HttpClientModule } from "@angular/common/http"; import { AppComponent } from "./app.component"; import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component"; -import { CollectTracesComponent } from "trace_collection/collect_traces.component"; -import { AdbProxyComponent } from "trace_collection/adb_proxy.component"; -import { WebAdbComponent } from "trace_collection/web_adb/web_adb.component"; -import { TraceConfigComponent } from "trace_collection/trace_config.component"; +import { CollectTracesComponent } from "./collect_traces.component"; +import { AdbProxyComponent } from "./adb_proxy.component"; +import { WebAdbComponent } from "./web_adb.component"; +import { TraceConfigComponent } from "./trace_config.component"; @NgModule({ declarations: [ diff --git a/tools/winscope-ng/src/trace_collection/collect_traces.component.spec.ts b/tools/winscope-ng/src/app/collect_traces.component.spec.ts similarity index 100% rename from tools/winscope-ng/src/trace_collection/collect_traces.component.spec.ts rename to tools/winscope-ng/src/app/collect_traces.component.spec.ts diff --git a/tools/winscope-ng/src/app/collect_traces.component.ts b/tools/winscope-ng/src/app/collect_traces.component.ts new file mode 100644 index 000000000..21bd42ab2 --- /dev/null +++ b/tools/winscope-ng/src/app/collect_traces.component.ts @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Component, Input, OnInit, Output, EventEmitter } from "@angular/core"; +import { ProxyConnection, Device, configureTraces } from "../trace_collection/connection"; +import { ProxyState } from "../trace_collection/proxy_client"; +import { traceConfigurations, configMap, SelectionConfiguration } from "../trace_collection/trace_collection_utils"; +import { Core } from "app/core"; +import { PersistentStore } from "../common/persistent_store"; + + +@Component({ + selector: "collect-traces", + template: ` + Collect Traces + + +
Connecting...
+ +
+ + + + +
+ +
+
{{ devices().length > 0 ? "Connected devices:" : "No devices detected" }}
+ + + + {{ connect.proxy.devices[deviceId].authorised ? "smartphone" : "screen_lock_portrait" }} + + + {{ connect.proxy.devices[deviceId].authorised ? connect.proxy.devices[deviceId].model : "unauthorised" }} ({{ deviceId }}) + + + +
+ +
+
+ + + smartphone + + {{ selectedDevice().model }} ({{ connect.proxy.selectedDevice }}) + + + +
+ +
+
+ + + +
+

Trace targets:

+ +
+ +
+

Dump targets:

+
+ {{connect.DUMPS[dumpKey].name}} +
+
+
+ +
+ error + Error: +
+            {{ connect.proxy.errorText }}
+        
+ +
+ +
+ Tracing... + + +
+ +
+ Loading data... + +
+ +
+ `, + styles: [".device-choice {cursor: pointer}"] +}) +export class CollectTracesComponent implements OnInit { + objectKeys = Object.keys; + isAdbProxy = true; + startTrace = false; + startDump = false; + traceConfigurations = traceConfigurations; + connect: any = new ProxyConnection(); + downloadProxyUrl = "https://android.googlesource.com/platform/development/+/master/tools/winscope-ng/adb/winscope_proxy.py"; + + @Input() + store: PersistentStore = new PersistentStore(); + + @Input() + core: Core = new Core(); + + @Output() + coreChange = new EventEmitter(); + + @Input() + dataLoaded: boolean = false; + + @Output() + dataLoadedChange = new EventEmitter(); + + ngOnInit(): void { + if (this.isAdbProxy) { + this.connect = new ProxyConnection(); + } else { + //TODO: change to WebAdbConnection + this.connect = new ProxyConnection(); + } + } + + ngOnDestroy(): void { + this.connect.proxy.removeOnProxyChange(this.onProxyChange); + } + + public onAddKey(key: string) { + this.store.addToStore("adb.proxyKey", key); + this.connect.setProxyKey(key); + this.restart(); + } + + public onProxyChange(newState: ProxyState) { + this.connect.onConnectChange(newState); + } + + public adbSuccess() { + return this.connect.adbSuccess(); + } + + public devices(): Array { + return this.connect.devices(); + } + + public selectedDevice(): Device { + return this.connect.selectedDevice(); + } + + public restart() { + this.connect.restart(); + } + + public resetLastDevice() { + this.connect.resetLastDevice(); + } + + public selectDevice(id: string) { + this.connect.selectDevice(id); + } + + public displayAdbProxyTab() { + this.isAdbProxy = true; + this.connect = new ProxyConnection(); + } + + public displayWebAdbTab() { + this.isAdbProxy = false; + //TODO: change to WebAdbConnection + this.connect = new ProxyConnection(); + } + + public requestedTraces() { + const tracesFromCollection: Array = []; + const req = Object.keys(this.connect.DYNAMIC_TRACES()) + .filter((traceKey:string) => { + const traceConfig = this.connect.DYNAMIC_TRACES()[traceKey]; + if (traceConfig.isTraceCollection) { + traceConfig.config.enableConfigs.forEach((innerTrace:any) => { + if (innerTrace.enabled) { + tracesFromCollection.push(innerTrace.key); + } + }); + return false; + } + return traceConfig.run; + }); + return req.concat(tracesFromCollection); + } + + public requestedDumps() { + return Object.keys(this.connect.DUMPS) + .filter((dumpKey:any) => { + return this.connect.DUMPS[dumpKey].enabled; + }); + } + + public requestedEnableConfig(): Array | null{ + const req: Array = []; + Object.keys(this.connect.DYNAMIC_TRACES()) + .forEach((traceKey:any) => { + const trace = this.connect.DYNAMIC_TRACES()[traceKey]; + if(!trace.isTraceCollection + && trace.run + && trace.config + && trace.config.enableConfigs) { + trace.config.enableConfigs.forEach((con:any) => { + if (con.enabled) { + req.push(con.key); + } + }); + } + }); + if (req.length === 0) { + return null; + } + return req; + } + + public requestedSelection(traceType: string) { + if (!this.connect.DYNAMIC_TRACES()[traceType].run) { + return null; + } + const selected: configMap = {}; + this.connect.DYNAMIC_TRACES()[traceType].config.selectionConfigs.forEach( + (con: SelectionConfiguration) => { + selected[con.key] = con.value; + } + ); + return selected; + } + + public startTracing() { + this.startTrace = true; + console.log("begin tracing"); + configureTraces.reqTraces = this.requestedTraces(); + const reqEnableConfig = this.requestedEnableConfig(); + const reqSelectedSfConfig = this.requestedSelection("layers_trace"); + const reqSelectedWmConfig = this.requestedSelection("window_trace"); + if (configureTraces.reqTraces.length < 1) { + this.connect.throwNoTargetsError(); + return; + } + this.connect.startTrace( + reqEnableConfig, + reqSelectedSfConfig, + reqSelectedWmConfig + ); + } + + public async dumpState() { + this.startDump = true; + console.log("begin dump"); + configureTraces.reqDumps = this.requestedDumps(); + await this.connect.dumpState(); + while (!this.connect.proxy.dataReady) { + await this.waitForData(1000); + } + await this.loadFiles(); + } + + public async endTrace() { + console.log("end tracing"); + await this.connect.endTrace(); + while (!this.connect.proxy.dataReady) { + await this.waitForData(1000); + } + await this.loadFiles(); + } + + public async loadFiles() { + console.log("loading files", this.connect.adbData()); + await this.core.bootstrap(this.connect.adbData()); + this.dataLoaded = true; + this.dataLoadedChange.emit(this.dataLoaded); + this.coreChange.emit(this.core); + console.log("finished loading data!"); + } + + public tabClass(adbTab: boolean) { + let isActive: string; + if (adbTab) { + isActive = this.isAdbProxy ? "active" : "inactive"; + } else { + isActive = !this.isAdbProxy ? "active" : "inactive"; + } + return ["tab", isActive]; + } + + private waitForData(ms: number) { + return new Promise( resolve => setTimeout(resolve, ms) ); + } +} diff --git a/tools/winscope-ng/src/app/core.ts b/tools/winscope-ng/src/app/core.ts index d3a305573..1d023ebc1 100644 --- a/tools/winscope-ng/src/app/core.ts +++ b/tools/winscope-ng/src/app/core.ts @@ -71,6 +71,11 @@ class Core { viewer.notifyCurrentTraceEntries(traceEntries); }); } + + clearData() { + this.parsers = []; + this.viewers = []; + } } export { Core }; diff --git a/tools/winscope-ng/src/trace_collection/trace_config.component.spec.ts b/tools/winscope-ng/src/app/trace_config.component.spec.ts similarity index 100% rename from tools/winscope-ng/src/trace_collection/trace_config.component.spec.ts rename to tools/winscope-ng/src/app/trace_config.component.spec.ts diff --git a/tools/winscope-ng/src/app/trace_config.component.ts b/tools/winscope-ng/src/app/trace_config.component.ts new file mode 100644 index 000000000..213f85c12 --- /dev/null +++ b/tools/winscope-ng/src/app/trace_config.component.ts @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Component, Input, OnChanges, SimpleChanges } from "@angular/core"; +import { EnableConfiguration, SelectionConfiguration, TraceConfiguration } from "../trace_collection/trace_collection_utils"; + +@Component({ + selector: "trace-config", + template: ` +
+ {{trace.name}} + +
+ {{enableConfig.name}} + +
+ {{selectionConfig.name}} + + {{ option }} + + +
+
+
+ `, + styles: [".adv-config {margin-left: 5rem;}"], +}) + +export class TraceConfigComponent { + @Input() + trace: TraceConfiguration = {}; + + public traceEnableConfigs(): Array { + if (this.trace.config) { + return this.trace.config.enableConfigs; + } else { + return []; + } + } + + public traceSelectionConfigs(): Array { + if (this.trace.config) { + return this.trace.config.selectionConfigs; + } else { + return []; + } + } + + public someTraces(): boolean { + return this.traceEnableConfigs().filter(trace => trace.enabled).length > 0 + && !this.trace.run; + } + + public changeRunTrace(run: boolean): void { + this.trace.run = run; + if (this.trace.isTraceCollection) { + this.traceEnableConfigs().forEach((c: EnableConfiguration) => (c.enabled = run)); + } + } + + public changeTraceCollectionConfig(): void { + if (this.trace.isTraceCollection) { + this.trace.run = this.traceEnableConfigs().every((c: EnableConfiguration) => c.enabled); + } + } +} diff --git a/tools/winscope-ng/src/trace_collection/web_adb/web_adb.component.spec.ts b/tools/winscope-ng/src/app/web_adb.component.spec.ts similarity index 100% rename from tools/winscope-ng/src/trace_collection/web_adb/web_adb.component.spec.ts rename to tools/winscope-ng/src/app/web_adb.component.spec.ts diff --git a/tools/winscope-ng/src/trace_collection/web_adb/web_adb.component.ts b/tools/winscope-ng/src/app/web_adb.component.ts similarity index 100% rename from tools/winscope-ng/src/trace_collection/web_adb/web_adb.component.ts rename to tools/winscope-ng/src/app/web_adb.component.ts diff --git a/tools/winscope-ng/src/styles.css b/tools/winscope-ng/src/styles.css index 8c859d63e..7886217ba 100644 --- a/tools/winscope-ng/src/styles.css +++ b/tools/winscope-ng/src/styles.css @@ -49,6 +49,10 @@ mat-icon { margin: 5px; } +.icon-message { + vertical-align: middle; +} + .card-block { margin: 15px; } diff --git a/tools/winscope-ng/src/trace_collection/collect_traces.component.ts b/tools/winscope-ng/src/trace_collection/collect_traces.component.ts deleted file mode 100644 index aeb96ff55..000000000 --- a/tools/winscope-ng/src/trace_collection/collect_traces.component.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Component, Input, OnInit} from "@angular/core"; -import { Connection, ProxyConnection, Device } from "./connection"; -import { ProxyState } from "./proxy_client"; -import { traceConfigurations, configMap } from "./trace_collection_utils"; -import { PersistentStore } from "../common/persistent_store"; - -@Component({ - selector: "collect-traces", - template: ` - Collect Traces - - -
Connecting...
- -
- - - - -
- -
-
{{ devices().length > 0 ? "Connected devices:" : "No devices detected" }}
- - - {{ connect.proxy.devices[deviceId].authorised ? "smartphone" : "screen_lock_portrait" }} - {{ connect.proxy.devices[deviceId].authorised ? connect.proxy.devices[deviceId].model : "unauthorised" }} ({{ deviceId }}) - - -
- -
-
- - - smartphone - {{ selectedDevice().model }} ({{ connect.proxy.selectedDevice }}) - - -
- -
-
- - - -
-

Trace targets:

- -
- -
-

Dump targets:

-
- {{DUMPS[dumpKey]}} -
-
-
- -
- error - Error: -
-            {{ errorText }}
-        
- -
- -
- Tracing... - -
-            {{ errorText }}
-        
- -
- -
- Loading data... - -
- -
- `, - styles: [".device-choice {cursor: pointer}"] -}) -export class CollectTracesComponent implements OnInit { - objectKeys = Object.keys; - isAdbProxy = true; - startTrace = false; - startDump = false; - errorText = ""; - traceConfigurations = traceConfigurations; - - connect: any = new ProxyConnection(); - - downloadProxyUrl = "https://android.googlesource.com/platform/development/+/master/tools/winscope-ng/adb/winscope_proxy.py"; - - states = ProxyState; - - @Input() - store: PersistentStore = new PersistentStore(); - - ngOnInit(): void { - if (this.isAdbProxy) { - this.connect = new ProxyConnection(); - } else { - //TODO: change to WebAdbConnection - this.connect = new ProxyConnection(); - } - } - - ngOnDestroy(): void { - this.connect.proxy.removeOnProxyChange(this.onProxyChange); - } - - public onAddKey(key: string) { - this.store.addToStore("adb.proxyKey", key); - this.connect.setProxyKey(key); - this.restart(); - } - - public onConnectChange(newState: Connection) { - this.connect.onConnectChange(newState); - } - - public onProxyChange(newState: ProxyState, errorText: string) { - this.connect.onConnectChange(newState); - } - - - public adbSuccess() { - return this.connect.adbSuccess(); - } - - public devices(): Array { - return this.connect.devices(); - } - - public selectedDevice(): Device { - return this.connect.selectedDevice(); - } - - public restart() { - this.connect.restart(); - } - - public resetLastDevice() { - this.connect.resetLastDevice(); - } - - public selectDevice(id: string) { - this.connect.selectDevice(id); - } - - public displayAdbProxyTab() { - this.isAdbProxy = true; - this.connect = new ProxyConnection(); - } - - public displayWebAdbTab() { - this.isAdbProxy = false; - //TODO: change to WebAdbConnection - this.connect = new ProxyConnection(); - } - - public startTracing() { - this.startTrace = true; - console.log("begin tracing"); - } - - public dumpState() { - this.startDump = true; - console.log("begin dump"); - } - - public endTrace() { - console.log("end trace"); - } - - public setAvailableTraces() { - this.connect.setAvailableTraces(); - } - - public tabClass(adbTab: boolean) { - let isActive: string; - if (adbTab) { - isActive = this.isAdbProxy ? "active" : "inactive"; - } else { - isActive = !this.isAdbProxy ? "active" : "inactive"; - } - return ["tab", isActive]; - } - - DYNAMIC_TRACES: any = null; - - DUMPS: configMap = { - "window_dump": "Window Manager", - "layers_dump": "Surface Flinger" - }; - -} \ No newline at end of file diff --git a/tools/winscope-ng/src/trace_collection/connection.ts b/tools/winscope-ng/src/trace_collection/connection.ts index 658d46c93..2ae50467f 100644 --- a/tools/winscope-ng/src/trace_collection/connection.ts +++ b/tools/winscope-ng/src/trace_collection/connection.ts @@ -1,5 +1,5 @@ -import { proxyClient, ProxyState, ProxyEndpoint, ProxyClient } from "trace_collection/proxy_client"; -import {TRACES, traceConfigurations} from "./trace_collection_utils"; +import { proxyRequest, proxyClient, ProxyState, ProxyEndpoint } from "trace_collection/proxy_client"; +import { TRACES } from "./trace_collection_utils"; export interface Device { authorised: boolean; @@ -13,24 +13,44 @@ export interface Connection { selectedDevice(): Device; restart(): any; selectDevice(id:string): any; - DYNAMIC_TRACES: any; + DYNAMIC_TRACES(): any; state(): ProxyState; onConnectChange(newState: any): any; - setAvailableTraces(): any; resetLastDevice(): any; + isDevicesState(): boolean; + isStartTraceState(): boolean; + isErrorState(): boolean; + isEndTraceState(): boolean; + isLoadDataState(): boolean; + isConnectingState(): boolean; + isNoProxy(): boolean; + isInvalidProxy(): boolean; + isUnauthProxy(): boolean; + throwNoTargetsError(): any; + startTrace( + reqEnableConfig?: Array, + reqSelectedSfConfig?: any, + reqSelectedWmConfig?: any + ): any; + DUMPS: any; + endTrace(): any; + dumpState(req:Array): any; + adbData(): any; } export class ProxyConnection implements Connection { - DYNAMIC_TRACES: any; proxy = proxyClient; - - public state() { - return this.proxy.state; - } - - public devices() { - return Object.keys(this.proxy.devices); - } + DUMPS: any = { + "window_dump": { + name: "Window Manager", + enabled: true, + }, + "layers_dump": { + name: "Surface Flinger", + enabled: true, + } + }; + keep_alive_worker: any = null; notConnected = [ ProxyState.NO_PROXY, ProxyState.UNAUTH, @@ -47,26 +67,68 @@ export class ProxyConnection implements Connection { this.proxy.getDevices(); } - public onConnectChange(newState: ProxyState) { - if (newState === ProxyState.CONNECTING) { - proxyClient.getDevices(); - } - if (newState == ProxyState.START_TRACE) { - proxyClient.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, function(request: any,view:any) { - try { - if(request.responseText == "true") { - //view.appendOptionalTraces('arc', view.TRACES); - } - } catch(err) { - console.error(err); - proxyClient.setState(ProxyState.ERROR, request.responseText); - } - }); - } + public devices() { + return Object.keys(this.proxy.devices); + } + + public adbData() { + return this.proxy.adbData; + } + + DYNAMIC_TRACES() { + return configureTraces.DYNAMIC_TRACES; + } + + public state() { + return this.proxy.state; + } + + public isDevicesState() { + return this.state() === ProxyState.DEVICES; + } + + public isStartTraceState() { + return this.state() === ProxyState.START_TRACE; + } + + public isErrorState() { + return this.state() === ProxyState.ERROR; + } + + public isEndTraceState() { + return this.state() === ProxyState.END_TRACE; + } + + public isLoadDataState() { + return this.state() === ProxyState.LOAD_DATA; + } + + public isConnectingState() { + return this.state() === ProxyState.CONNECTING; + } + + public isNoProxy() { + return this.state() === ProxyState.NO_PROXY; + } + + public isInvalidProxy() { + return this.state() === ProxyState.INVALID_VERSION; + } + + public isUnauthProxy() { + return this.state() === ProxyState.UNAUTH; + } + + public throwNoTargetsError() { + this.proxy.setState(ProxyState.ERROR, "No targets selected"); + } + + public dataReady() { + return this.proxy.dataReady; } public setProxyKey(key: string) { - proxyClient.proxyKey = key; + this.proxy.proxyKey = key; this.restart(); } @@ -83,7 +145,7 @@ export class ProxyConnection implements Connection { } public resetLastDevice() { - this.proxy.resetLastDevice(); + this.proxy.store.addToStore("adb.lastDevice", ""); this.restart(); } @@ -91,17 +153,82 @@ export class ProxyConnection implements Connection { this.proxy.selectDevice(id); } - public setAvailableTraces() { - this.DYNAMIC_TRACES = TRACES["default"]; - proxyClient.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, function(request:any, view:any) { - try { - if(request.responseText == "true") { - //view.appendOptionalTraces('arc'); - } - } catch(err) { - console.error(err); - proxyClient.setState(ProxyState.ERROR, request.responseText); + public keepAliveTrace(view:any) { + if (!view.isEndTraceState()) { + clearInterval(view.keep_alive_worker); + view.keep_alive_worker = null; + return; + } + proxyRequest.call("GET", `${ProxyEndpoint.STATUS}${view.proxy.selectedDevice}/`, view, function(request:any, newView:any) { + if (request.responseText !== "True") { + newView.endTrace(); + } else if (newView.keep_alive_worker === null) { + newView.keep_alive_worker = setInterval(newView.keepAliveTrace, 1000, newView); } }); } + + public startTrace( + reqEnableConfig: any, + reqSelectedSfConfig: any, + reqSelectedWmConfig: any + ) { + if (reqEnableConfig) { + proxyRequest.call("POST", `${ProxyEndpoint.CONFIG_TRACE}${this.proxy.selectedDevice}/`, this, null, null, reqEnableConfig); + } + if (reqSelectedSfConfig) { + proxyRequest.call("POST", `${ProxyEndpoint.SELECTED_SF_CONFIG_TRACE}${this.proxy.selectedDevice}/`, this, null, null, reqSelectedSfConfig); + } + if (reqSelectedWmConfig) { + proxyRequest.call("POST", `${ProxyEndpoint.SELECTED_WM_CONFIG_TRACE}${this.proxy.selectedDevice}/`, this, null, null, reqSelectedWmConfig); + } + proxyClient.setState(ProxyState.END_TRACE); + proxyRequest.call("POST", `${ProxyEndpoint.START_TRACE}${this.proxy.selectedDevice}/`, this, function(request:any, view:any) { + view.keepAliveTrace(view); + }, null, configureTraces.reqTraces); + } + + public async endTrace() { + this.proxy.setState(ProxyState.LOAD_DATA); + await proxyRequest.call("POST", `${ProxyEndpoint.END_TRACE}${this.proxy.selectedDevice}/`, this, + async function (request:any, view:any) { + await proxyClient.updateAdbData(configureTraces.reqTraces, 0, "trace", view); + }); + } + + public dumpState() { + if (configureTraces.reqDumps.length < 1) { + this.proxy.setState(ProxyState.ERROR, "No targets selected"); + return; + } + this.proxy.setState(ProxyState.LOAD_DATA); + proxyRequest.call("POST", `${ProxyEndpoint.DUMP}${this.proxy.selectedDevice}/`, this, function(request:any, view:any) { + view.proxy.updateAdbData(configureTraces.reqDumps, 0, "dump", view); + }, null, configureTraces.reqDumps); + } + + public onConnectChange(newState: ProxyState) { + if (newState === ProxyState.CONNECTING) { + proxyClient.getDevices(); + } + if (newState == ProxyState.START_TRACE) { + configureTraces.setAvailableTraces(); + } + } } + +class ConfigureTraces { + DYNAMIC_TRACES = TRACES["default"]; + reqTraces: string[] = []; + reqDumps: string[] = []; + + setAvailableTraces() { + proxyRequest.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, proxyRequest.setAvailableTraces); + } + appendOptionalTraces(view:any, device_key:string) { + for(const key in TRACES[device_key]) { + view.DYNAMIC_TRACES[key] = TRACES[device_key][key]; + } + } +} +export const configureTraces = new ConfigureTraces(); \ No newline at end of file diff --git a/tools/winscope-ng/src/trace_collection/proxy_client.ts b/tools/winscope-ng/src/trace_collection/proxy_client.ts index de906035a..43e0008ee 100644 --- a/tools/winscope-ng/src/trace_collection/proxy_client.ts +++ b/tools/winscope-ng/src/trace_collection/proxy_client.ts @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { PersistentStore } from "common/persistent_store"; +import { PersistentStore } from "../common/persistent_store"; +import { TRACES } from "./trace_collection_utils"; export enum ProxyState { ERROR = 0, @@ -40,25 +41,11 @@ export enum ProxyEndpoint { CHECK_WAYLAND = "/checkwayland/", } -export class ProxyClient { - readonly WINSCOPE_PROXY_URL = "http://localhost:5544"; - readonly VERSION = "0.8"; - - state: ProxyState = ProxyState.CONNECTING; - stateChangeListeners: {(param:ProxyState, errorText:string): void;}[] = []; - refresh_worker: NodeJS.Timeout | undefined = undefined; - devices: any = {}; - selectedDevice = ""; - errorText = ""; - - proxyKey = ""; - lastDevice = ""; - - store = new PersistentStore(); - - call(method: string, path: string, view: any, onSuccess: any, type = null, jsonRequest = null) { +// from here, all requests to the proxy are made +class ProxyRequest { + async call(method: string, path: string, view: any, onSuccess: any, type: any = null, jsonRequest:any = null) { const request = new XMLHttpRequest(); - const client = this; + const client = proxyClient; request.onreadystatechange = function() { if (this.readyState !== 4) { return; @@ -79,7 +66,7 @@ export class ProxyClient { } else if (this.responseType === "arraybuffer") { client.errorText = String.fromCharCode.apply(null, new Array(this.response)); } - client.setState(ProxyState.ERROR); + client.setState(ProxyState.ERROR, client.errorText); } }; request.responseType = type || ""; @@ -98,6 +85,88 @@ export class ProxyClient { } } + getDevices = function(request: any, view: any) { + const client = proxyClient; + try { + client.devices = JSON.parse(request.responseText); + const last = client.store.getFromStore("adb.lastDevice"); + if (last && client.devices[last] && + client.devices[last].authorised) { + client.selectDevice(last); + } else { + if (client.refresh_worker === null) { + client.refresh_worker = setInterval(client.getDevices, 1000); + } + client.setState(ProxyState.DEVICES); + } + } catch (err) { + console.error(err); + client.errorText = request.responseText; + client.setState(ProxyState.ERROR, client.errorText); + } + }; + + setAvailableTraces = function(request:any, view:any) { + try { + view.DYNAMIC_TRACES = TRACES["default"]; + if(request.responseText == "true") { + view.appendOptionalTraces(view, "arc"); + } + } catch(err) { + proxyClient.setState(ProxyState.ERROR, request.responseText); + } + }; + + updateAdbData = async (request: any, view: any) => { + let idx = proxyClient.adbParams.idx; + let files = proxyClient.adbParams.files; + let traceType = proxyClient.adbParams.traceType; + try { + const enc = new TextDecoder("utf-8"); + const resp = enc.decode(request.response); + const filesByType = JSON.parse(resp); + + for (const filetype in filesByType) { + const files = filesByType[filetype]; + for (const encodedFileBuffer of files) { + const buffer = Uint8Array.from(atob(encodedFileBuffer), (c) => c.charCodeAt(0)); + const blob = new Blob([buffer]); + proxyClient.adbData.push(blob); + } + } + if (idx < files.length - 1) { + proxyClient.updateAdbData(files, idx + 1, traceType, view); + } else { + proxyClient.dataReady = true; + } + } catch (error) { + proxyClient.setState(ProxyState.ERROR, request.responseText); + } + } +} +export const proxyRequest = new ProxyRequest(); + +// stores all the changing variables from proxy and sets up calls from ProxyRequest +export class ProxyClient { + readonly WINSCOPE_PROXY_URL = "http://localhost:5544"; + readonly VERSION = "0.8"; + state: ProxyState = ProxyState.CONNECTING; + stateChangeListeners: {(param:ProxyState, errorText:string): void;}[] = []; + refresh_worker: NodeJS.Timer | null = null; + devices: any = {}; + selectedDevice = ""; + errorText = ""; + adbData: Array = []; + proxyKey = ""; + lastDevice = ""; + store = new PersistentStore(); + dataReady: boolean = false; + adbParams = { + files: [], + idx: -1, + traceType: null, + }; + setState(state:ProxyState, errorText = "") { this.state = state; this.errorText = errorText; @@ -117,46 +186,25 @@ export class ProxyClient { getDevices() { if (this.state !== ProxyState.DEVICES && this.state !== ProxyState.CONNECTING) { - clearInterval(this.refresh_worker); - this.refresh_worker = undefined; + clearInterval(this.refresh_worker!); + this.refresh_worker = null; return; } - const client = this; - this.call("GET", ProxyEndpoint.DEVICES, this, function(request: any, view: any) { - try { - client.devices = JSON.parse(request.responseText); - const last = client.store.getFromStore("adb.lastDevice"); - if (last && client.devices[last] && - client.devices[last].authorised) { - client.selectDevice(last); - } else { - if (client.refresh_worker === undefined) { - client.refresh_worker = setInterval(client.getDevices, 1000); - } - client.setState(ProxyState.DEVICES); - } - } catch (err) { - console.error(err); - client.errorText = request.responseText; - client.setState(ProxyState.ERROR); - } - }); + proxyRequest.call("GET", ProxyEndpoint.DEVICES, this, proxyRequest.getDevices); } selectDevice(device_id: string) { this.selectedDevice = device_id; this.store.addToStore("adb.lastDevice", device_id); - this.lastDevice = device_id; this.setState(ProxyState.START_TRACE); } - deviceId() { - return this.selectedDevice; - } - - resetLastDevice() { - this.lastDevice = ""; - this.store.addToStore("adb.lastDevice", ""); + async updateAdbData(files:any, idx:any, traceType:any, view: any) { + this.adbParams.files = files; + this.adbParams.idx = idx; + this.adbParams.traceType = traceType; + await proxyRequest.call("GET", `${ProxyEndpoint.FETCH}${this.selectedDevice}/${files[idx]}/`, view, + proxyRequest.updateAdbData, "arraybuffer"); } } diff --git a/tools/winscope-ng/src/trace_collection/trace_collection_utils.ts b/tools/winscope-ng/src/trace_collection/trace_collection_utils.ts index 99a78472a..28c4201a0 100644 --- a/tools/winscope-ng/src/trace_collection/trace_collection_utils.ts +++ b/tools/winscope-ng/src/trace_collection/trace_collection_utils.ts @@ -1,10 +1,14 @@ - -interface TraceConfiguration { - name: string, - defaultCheck?: boolean, +export interface TraceConfiguration { + name?: string, + run?: boolean, + isTraceCollection?: boolean, config?: ConfigurationOptions } +interface TraceConfigurationMap { + [key: string]: TraceConfiguration +} + export interface ConfigurationOptions { enableConfigs: Array, selectionConfigs: Array @@ -12,10 +16,12 @@ export interface ConfigurationOptions { export interface EnableConfiguration { name: string, - defaultCheck: boolean, + key: string, + enabled: boolean, } export interface SelectionConfiguration { + key: string, name: string, options: Array, value: string @@ -25,46 +31,10 @@ export type configMap = { [key: string]: Array | string; } -export const TRACES = { - "default": { - "window_trace": { - name: "Window Manager", - }, - "accessibility_trace": { - name: "Accessibility", - }, - "layers_trace": { - name: "Surface Flinger", - }, - "transactions": { - name: "Transaction", - }, - "proto_log": { - name: "ProtoLog", - }, - "screen_recording": { - name: "Screen Recording", - }, - "ime_trace_clients": { - name: "Input Method Clients", - }, - "ime_trace_service": { - name: "Input Method Service", - }, - "ime_trace_managerservice": { - name: "Input Method Manager Service", - }, - }, - "arc": { - "wayland_trace": { - name: "Wayland", - }, - }, -}; - -const wmTraceSelectionConfigs = [ +const wmTraceSelectionConfigs: Array = [ { - name: "wmbuffersize (KB)", + key: "wmbuffersize", + name: "buffer size (KB)", options: [ "4000", "8000", @@ -74,7 +44,8 @@ const wmTraceSelectionConfigs = [ value: "4000" }, { - name: "tracingtype", + key: "tracingtype", + name: "tracing type", options: [ "frame", "transaction", @@ -82,7 +53,8 @@ const wmTraceSelectionConfigs = [ value: "frame" }, { - name: "tracinglevel", + key: "tracinglevel", + name: "tracing level", options: [ "verbose", "debug", @@ -92,54 +64,126 @@ const wmTraceSelectionConfigs = [ }, ]; - -export const traceConfigurations: Array = [ +const sfTraceEnableConfigs: Array = [ { - name: "Surface Flinger", - defaultCheck: true, - config: { - enableConfigs: [ - {name: "composition", defaultCheck: false}, - {name: "metadata", defaultCheck: false}, - {name: "hwc", defaultCheck: false}, - {name: "tracebuffers", defaultCheck: false} - ], - selectionConfigs: [ - { - name: "sfbuffersize (KB)", - options: ["4000","8000","16000","32000",], - value: "4000" - } - ] - } + name: "composition", + key: "composition", + enabled: true }, { + name: "metadata", + key: "metadata", + enabled: true + }, + { + name: "hwc", + key: "hwc", + enabled: true + }, + { + name: "trace buffers", + key: "tracebuffers", + enabled: true + } +]; + +const sfTraceSelectionConfigs: Array = [ + { + key: "sfbuffersize", + name: "buffer size (KB)", + options: ["4000","8000","16000","32000"], + value: "4000" + } +]; + +export const traceConfigurations: TraceConfigurationMap = { + "layers_trace": { + name: "Surface Flinger", + run: true, + config: { + enableConfigs: sfTraceEnableConfigs, + selectionConfigs: sfTraceSelectionConfigs, + } + }, + "window_trace": { name: "Window Manager", - defaultCheck: true, + run: true, config: { enableConfigs: [], selectionConfigs: wmTraceSelectionConfigs, } }, - { + "screen_recording": { name: "Screen Recording", + run: true, }, - { - name: "Accessibility", - }, - { - name: "Transaction", - }, - { + "ime_tracing": { name: "IME Tracing", - defaultCheck: true, + run: true, + isTraceCollection: true, config: { enableConfigs: [ - {name: "Input Method Clients", defaultCheck: true}, - {name: "Input Method Service", defaultCheck: true}, - {name: "Input Method Manager Service", defaultCheck: true}, + { + name: "Input Method Clients", + key: "ime_trace_clients", + enabled: true, + }, + { + name: "Input Method Service", + key: "ime_trace_service", + enabled: true, + }, + { + name: "Input Method Manager Service", + key: "ime_trace_managerservice", + enabled: true, + }, ], selectionConfigs: [] } }, -]; + "ime_trace_clients": { + name: "Input Method Clients", + run: true, + }, + "ime_trace_service": { + name: "Input Method Service", + run: true, + }, + "ime_trace_managerservice": { + name: "Input Method Manager Service", + run: true, + }, + "accessibility_trace": { + name: "Accessibility", + run: false, + }, + "transactions": { + name: "Transaction", + run: false, + }, + "proto_log": { + name: "ProtoLog", + run: false, + }, + "wayland_trace": { + name: "Wayland", + run: false, + }, +}; + + +export const TRACES: { [key: string]: TraceConfigurationMap; } = { + "default": { + "window_trace": traceConfigurations["window_trace"], + "accessibility_trace": traceConfigurations["accessibility_trace"], + "layers_trace": traceConfigurations["layers_trace"], + "transactions": traceConfigurations["transactions"], + "proto_log": traceConfigurations["proto_log"], + "screen_recording": traceConfigurations["screen_recording"], + "ime_tracing": traceConfigurations["ime_tracing"], + }, + "arc": { + "wayland_trace": traceConfigurations["wayland_trace"], + }, +}; diff --git a/tools/winscope-ng/src/trace_collection/trace_config.component.ts b/tools/winscope-ng/src/trace_collection/trace_config.component.ts deleted file mode 100644 index 8b2ce9606..000000000 --- a/tools/winscope-ng/src/trace_collection/trace_config.component.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ConfigurationOptions, SelectionConfiguration } from "./trace_collection_utils"; -import { Component, Input, Output, EventEmitter } from "@angular/core"; -import { ProxyState, ProxyClient } from "./proxy_client"; - - -@Component({ - selector: "trace-config", - template: ` -
- {{name}} -
- {{enableConfig.name}} -
- - {{con.name}} - - {{ option }} - - -
-
-
- `, - styles: [".adv-config {margin-left: 5rem;}"], -}) - -export class TraceConfigComponent { - states = ProxyState; - objectKeys = Object.keys; - - @Input() - name = ""; - - @Input() - configs: ConfigurationOptions | null = null; - - @Input() - defaultCheck: boolean | undefined = false; - - public traceEnableConfigs(): Array { - if (this.configs && this.configs.enableConfigs) { - return this.configs.enableConfigs; - } else { - return []; - } - } - - public traceSelectionConfigs(): Array { - if (this.configs) { - return this.configs.selectionConfigs; - } else { - return []; - } - } -} \ No newline at end of file