Adding ability to run and end traces via proxy.
Finishing proxy migration by creating functionality to run and end traces and dumps after selecting trace config. Test: Connect a device via remote device proxy and follow the proxy workflow for a trace or dump. Should see a placeholder message saying data loaded (trace views not yet created). Bug: b/238113543 Change-Id: Ic7e0948341511f6ec0bf1021d2ffbb7b198c9410
This commit is contained in:
@@ -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";
|
||||
<p>Python 3.5+ and ADB are required.</p>
|
||||
<p>Run:</p>
|
||||
<pre>python3</pre>
|
||||
<pre>$ANDROID_BUILD_TOP/development/tools/winscope-ng/adb/winscope_proxy.py</pre>
|
||||
<pre>$ANDROID_BUILD_TOP/development/tools/winscope-ng/src/adb/winscope_proxy.py</pre>
|
||||
<p>Or get it from the AOSP repository.</p>
|
||||
</div>
|
||||
<div class="md-layout">
|
||||
@@ -42,14 +42,14 @@ import { ProxyClient, ProxyState } from "./proxy_client";
|
||||
|
||||
<div *ngIf="proxy.state===states.INVALID_VERSION">
|
||||
<div>
|
||||
<mat-icon>update</mat-icon>
|
||||
<mat-icon class="icon-message">update</mat-icon>
|
||||
<span class="icon-message">Your local proxy version is incompatible with Winscope.</span>
|
||||
</div>
|
||||
<div class="md-body-2" layout="layout-md">
|
||||
<p>Please update the proxy to version {{ proxyVersion }}.</p>
|
||||
<p>Run:</p>
|
||||
<pre>python3</pre>
|
||||
<pre>$ANDROID_BUILD_TOP/development/tools/winscope-ng/adb/winscope_proxy.py</pre>
|
||||
<pre>$ANDROID_BUILD_TOP/development/tools/winscope-ng/src/adb/winscope_proxy.py</pre>
|
||||
<p>Or get it from the AOSP repository.</p>
|
||||
</div>
|
||||
<div class="md-layout">
|
||||
@@ -62,7 +62,7 @@ import { ProxyClient, ProxyState } from "./proxy_client";
|
||||
|
||||
<div *ngIf="proxy.state===states.UNAUTH">
|
||||
<div>
|
||||
<mat-icon>lock</mat-icon>
|
||||
<mat-icon class="icon-message">lock</mat-icon>
|
||||
<span class="icon-message">Proxy authorisation required</span>
|
||||
</div>
|
||||
<div class="md-body-2" layout="layout-md">
|
||||
@@ -78,23 +78,20 @@ import { ProxyClient, ProxyState } from "./proxy_client";
|
||||
</div>
|
||||
|
||||
`,
|
||||
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<ProxyClient>();
|
||||
proxyChange = new EventEmitter<ProxyClient>();
|
||||
|
||||
@Output() addKey = new EventEmitter<string>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ import { PersistentStore } from "../common/persistent_store";
|
||||
<div id="title">
|
||||
<span>Winscope Viewer 2.0</span>
|
||||
</div>
|
||||
<div class="card-container" fxLayout="row wrap" fxLayoutGap="10px grid">
|
||||
<div *ngIf="!dataLoaded" class="card-container" fxLayout="row wrap" fxLayoutGap="10px grid">
|
||||
<mat-card class="homepage-card">
|
||||
<collect-traces [store]="store"></collect-traces>
|
||||
<collect-traces [(core)]="core" [(dataLoaded)]="dataLoaded" [store]="store"></collect-traces>
|
||||
</mat-card>
|
||||
<mat-card class="homepage-card">
|
||||
<mat-card-title>Upload Traces</mat-card-title>
|
||||
@@ -38,10 +38,11 @@ import { PersistentStore } from "../common/persistent_store";
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
<div id="inputfile">
|
||||
<button type="button" mat-raised-button (click)="fileInput.click()">Choose File</button>
|
||||
<input hidden (change)="onInputFile($event)" #fileInput type="file" id="file">
|
||||
<input type="file" (change)="onInputFile($event)" #fileUpload>
|
||||
<div *ngIf="dataLoaded">
|
||||
<mat-card class="homepage-card">
|
||||
<mat-card-title>Loaded data</mat-card-title>
|
||||
<button mat-raised-button (click)="clearData()">Back to Home</button>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
<div id="timescrub">
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: [
|
||||
|
||||
316
tools/winscope-ng/src/app/collect_traces.component.ts
Normal file
316
tools/winscope-ng/src/app/collect_traces.component.ts
Normal file
@@ -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: `
|
||||
<mat-card-title>Collect Traces</mat-card-title>
|
||||
<mat-card-content>
|
||||
|
||||
<div *ngIf="connect.isConnectingState()">Connecting...</div>
|
||||
|
||||
<div id="set-up-adb" *ngIf="!adbSuccess()">
|
||||
<button mat-raised-button [ngClass]="tabClass(true)" (click)="displayAdbProxyTab()">ADB Proxy</button>
|
||||
<button mat-raised-button [ngClass]="tabClass(false)" (click)="displayWebAdbTab()">Web ADB</button>
|
||||
<adb-proxy *ngIf="isAdbProxy" [(proxy)]="connect.proxy" (addKey)="onAddKey($event)"></adb-proxy>
|
||||
<web-adb *ngIf="!isAdbProxy"></web-adb>
|
||||
</div>
|
||||
|
||||
<div id="devices-connecting" *ngIf="connect.isDevicesState()">
|
||||
<div> {{ devices().length > 0 ? "Connected devices:" : "No devices detected" }}</div>
|
||||
<mat-list class="device-choice">
|
||||
<mat-list-item *ngFor="let deviceId of devices()" (click)="selectDevice(deviceId)">
|
||||
<mat-icon class="icon-message">
|
||||
{{ connect.proxy.devices[deviceId].authorised ? "smartphone" : "screen_lock_portrait" }}
|
||||
</mat-icon>
|
||||
<span class="icon-message">
|
||||
{{ connect.proxy.devices[deviceId].authorised ? connect.proxy.devices[deviceId].model : "unauthorised" }} ({{ deviceId }})
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
|
||||
<div id="trace-collection-config" *ngIf="connect.isStartTraceState()">
|
||||
<div class="device-choice">
|
||||
<mat-list class="device-choice">
|
||||
<mat-list-item>
|
||||
<mat-icon class="icon-message">smartphone</mat-icon>
|
||||
<span class="icon-message">
|
||||
{{ selectedDevice().model }} ({{ connect.proxy.selectedDevice }})
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
|
||||
<div class="trace-section">
|
||||
<div class="md-layout">
|
||||
<button mat-raised-button class="md-accent" (click)="startTracing()">Start Trace</button>
|
||||
<button mat-raised-button (click)="dumpState()">Dump State</button>
|
||||
<button mat-raised-button class="md-primary" (click)="resetLastDevice()">Change Device</button>
|
||||
</div>
|
||||
<h3>Trace targets:</h3>
|
||||
<trace-config
|
||||
*ngFor="let traceKey of objectKeys(connect.DYNAMIC_TRACES())"
|
||||
[trace]="connect.DYNAMIC_TRACES()[traceKey]"
|
||||
></trace-config>
|
||||
</div>
|
||||
|
||||
<div class="dump-section">
|
||||
<h3>Dump targets:</h3>
|
||||
<div class="selection">
|
||||
<mat-checkbox
|
||||
class="md-primary"
|
||||
*ngFor="let dumpKey of objectKeys(connect.DUMPS)"
|
||||
[(ngModel)]="connect.DUMPS[dumpKey].enabled"
|
||||
>{{connect.DUMPS[dumpKey].name}}</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="unknown-error" *ngIf="connect.isErrorState()">
|
||||
<mat-icon class="icon-message">error</mat-icon>
|
||||
<span class="icon-message">Error:</span>
|
||||
<pre>
|
||||
{{ connect.proxy.errorText }}
|
||||
</pre>
|
||||
<button mat-raised-button (click)="restart()">Retry</button>
|
||||
</div>
|
||||
|
||||
<div id="end-tracing" *ngIf="connect.isEndTraceState()">
|
||||
<span class="md-subheading">Tracing...</span>
|
||||
<mat-progress-bar md-indeterminate value="{{connect.loadProgress}}"></mat-progress-bar>
|
||||
<button mat-raised-button (click)="endTrace()">End trace</button>
|
||||
</div>
|
||||
|
||||
<div id="load-data" *ngIf="connect.isLoadDataState()">
|
||||
<span class="md-subheading">Loading data...</span>
|
||||
<mat-progress-bar md-indeterminate></mat-progress-bar>
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
`,
|
||||
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<Core>();
|
||||
|
||||
@Input()
|
||||
dataLoaded: boolean = false;
|
||||
|
||||
@Output()
|
||||
dataLoadedChange = new EventEmitter<boolean>();
|
||||
|
||||
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<string> {
|
||||
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<any> = [];
|
||||
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<string> | null{
|
||||
const req: Array<string> = [];
|
||||
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) );
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,11 @@ class Core {
|
||||
viewer.notifyCurrentTraceEntries(traceEntries);
|
||||
});
|
||||
}
|
||||
|
||||
clearData() {
|
||||
this.parsers = [];
|
||||
this.viewers = [];
|
||||
}
|
||||
}
|
||||
|
||||
export { Core };
|
||||
|
||||
94
tools/winscope-ng/src/app/trace_config.component.ts
Normal file
94
tools/winscope-ng/src/app/trace_config.component.ts
Normal file
@@ -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: `
|
||||
<div class="card-block">
|
||||
<mat-checkbox
|
||||
[checked]="trace.run"
|
||||
[indeterminate]="trace.isTraceCollection ? someTraces() : false"
|
||||
(change)="changeRunTrace($event.checked)"
|
||||
>{{trace.name}}</mat-checkbox>
|
||||
|
||||
<div class="adv-config" *ngIf="trace.config">
|
||||
<mat-checkbox
|
||||
*ngFor="let enableConfig of traceEnableConfigs()"
|
||||
[disabled]="!trace.run && !trace.isTraceCollection"
|
||||
[(ngModel)]="enableConfig.enabled"
|
||||
(ngModelChange)="changeTraceCollectionConfig()"
|
||||
>{{enableConfig.name}}</mat-checkbox>
|
||||
|
||||
<div class="selection" *ngIf="trace.config.selectionConfigs">
|
||||
<mat-form-field
|
||||
appearance="fill"
|
||||
class="config-selection"
|
||||
*ngFor="let selectionConfig of traceSelectionConfigs()"
|
||||
><mat-label>{{selectionConfig.name}}</mat-label>
|
||||
<mat-select [(value)]="selectionConfig.value" [disabled]="!trace.run">
|
||||
<mat-option
|
||||
*ngFor="let option of selectionConfig.options"
|
||||
value="{{option}}"
|
||||
>{{ option }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [".adv-config {margin-left: 5rem;}"],
|
||||
})
|
||||
|
||||
export class TraceConfigComponent {
|
||||
@Input()
|
||||
trace: TraceConfiguration = {};
|
||||
|
||||
public traceEnableConfigs(): Array<EnableConfiguration> {
|
||||
if (this.trace.config) {
|
||||
return this.trace.config.enableConfigs;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public traceSelectionConfigs(): Array<SelectionConfiguration> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,10 @@ mat-icon {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.icon-message {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
@@ -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: `
|
||||
<mat-card-title>Collect Traces</mat-card-title>
|
||||
<mat-card-content>
|
||||
|
||||
<div *ngIf="connect.state()===states.CONNECTING">Connecting...</div>
|
||||
|
||||
<div id="set-up-adb" *ngIf="!adbSuccess()">
|
||||
<button mat-raised-button [ngClass]="tabClass(true)" (click)="displayAdbProxyTab()">ADB Proxy</button>
|
||||
<button mat-raised-button [ngClass]="tabClass(false)" (click)="displayWebAdbTab()">Web ADB</button>
|
||||
<adb-proxy *ngIf="isAdbProxy" [(proxy)]="connect.proxy" (addKey)="onAddKey($event)"></adb-proxy>
|
||||
<web-adb *ngIf="!isAdbProxy"></web-adb>
|
||||
</div>
|
||||
|
||||
<div id="devices-connecting" *ngIf="connect.state()===states.DEVICES">
|
||||
<div> {{ devices().length > 0 ? "Connected devices:" : "No devices detected" }}</div>
|
||||
<mat-list class="device-choice">
|
||||
<mat-list-item *ngFor="let deviceId of devices()" (click)="selectDevice(deviceId)">
|
||||
<mat-icon>{{ connect.proxy.devices[deviceId].authorised ? "smartphone" : "screen_lock_portrait" }}</mat-icon>
|
||||
<span class="md-list-item-text">{{ connect.proxy.devices[deviceId].authorised ? connect.proxy.devices[deviceId].model : "unauthorised" }} ({{ deviceId }})</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
|
||||
<div id="trace-collection-config" *ngIf="connect.state()===states.START_TRACE">
|
||||
<div class="device-choice">
|
||||
<mat-list class="device-choice">
|
||||
<mat-list-item>
|
||||
<mat-icon>smartphone</mat-icon>
|
||||
<span class="md-list-item-text">{{ selectedDevice().model }} ({{ connect.proxy.selectedDevice }})</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
|
||||
<div class="trace-section">
|
||||
<div class="md-layout">
|
||||
<button mat-raised-button class="md-accent" (click)="startTracing()">Start Trace</button>
|
||||
<button mat-raised-button (click)="dumpState()">Dump State</button>
|
||||
<button mat-raised-button class="md-primary" (click)="resetLastDevice()">Change Device</button>
|
||||
</div>
|
||||
<h3>Trace targets:</h3>
|
||||
<trace-config
|
||||
*ngFor="let trace of traceConfigurations"
|
||||
[name]="trace.name"
|
||||
[defaultCheck]="trace.defaultCheck"
|
||||
[configs]="trace.config ? trace.config : null"
|
||||
></trace-config>
|
||||
</div>
|
||||
|
||||
<div class="dump-section">
|
||||
<h3>Dump targets:</h3>
|
||||
<div class="selection">
|
||||
<mat-checkbox class="md-primary" *ngFor="let dumpKey of objectKeys(DUMPS)" [checked]="true">{{DUMPS[dumpKey]}}</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="unknown-error" *ngIf="connect.state()===states.ERROR">
|
||||
<mat-icon>error</mat-icon>
|
||||
<span class="md-subheading">Error:</span>
|
||||
<pre>
|
||||
{{ errorText }}
|
||||
</pre>
|
||||
<button mat-raised-button (click)="restart()">Retry</button>
|
||||
</div>
|
||||
|
||||
<div id="end-tracing" *ngIf="connect.state()===states.END_TRACE">
|
||||
<span class="md-subheading">Tracing...</span>
|
||||
<mat-progress-bar md-indeterminate></mat-progress-bar>
|
||||
<pre>
|
||||
{{ errorText }}
|
||||
</pre>
|
||||
<button mat-raised-button (click)="endTrace()">End trace</button>
|
||||
</div>
|
||||
|
||||
<div id="load-data" *ngIf="connect.state()===states.LOAD_DATA">
|
||||
<span class="md-subheading">Loading data...</span>
|
||||
<mat-progress-bar md-indeterminate></mat-progress-bar>
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
`,
|
||||
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<string> {
|
||||
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"
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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<string>,
|
||||
reqSelectedSfConfig?: any,
|
||||
reqSelectedWmConfig?: any
|
||||
): any;
|
||||
DUMPS: any;
|
||||
endTrace(): any;
|
||||
dumpState(req:Array<string>): 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();
|
||||
@@ -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<any> = [];
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<EnableConfiguration>,
|
||||
selectionConfigs: Array<SelectionConfiguration>
|
||||
@@ -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<string>,
|
||||
value: string
|
||||
@@ -25,46 +31,10 @@ export type configMap = {
|
||||
[key: string]: Array<string> | 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<SelectionConfiguration> = [
|
||||
{
|
||||
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<TraceConfiguration> = [
|
||||
const sfTraceEnableConfigs: Array<EnableConfiguration> = [
|
||||
{
|
||||
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<SelectionConfiguration> = [
|
||||
{
|
||||
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"],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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: `
|
||||
<div class="card-block">
|
||||
<mat-checkbox class="md-primary" [checked]="defaultCheck">{{name}}</mat-checkbox>
|
||||
<div class="adv-config" *ngIf="configs">
|
||||
<mat-checkbox class="md-primary" *ngFor="let enableConfig of traceEnableConfigs()" [checked]="enableConfig.defaultCheck">{{enableConfig.name}}</mat-checkbox>
|
||||
<div class="selection">
|
||||
<mat-form-field appearance="fill" class="config-selection" *ngFor="let con of traceSelectionConfigs()">
|
||||
<mat-label>{{con.name}}</mat-label>
|
||||
<mat-select>
|
||||
<mat-option
|
||||
*ngFor="let option of con.options"
|
||||
[value]="con.value"
|
||||
>{{ option }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
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<any> {
|
||||
if (this.configs && this.configs.enableConfigs) {
|
||||
return this.configs.enableConfigs;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public traceSelectionConfigs(): Array<SelectionConfiguration> {
|
||||
if (this.configs) {
|
||||
return this.configs.selectionConfigs;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user