Add tags/errors to trace views.
At the start or end timestamp of a transition, or at an error timestamp, colour coded tags show up
next to the relevant layer/task in the WM and SF trace hierarchy views.
Tick the transition checkbox to only show entries that have tags/errors.
Compatible with flat checkbox.
Bug: b/196544612
Test: upload the zip onto the x20 (see bug) and test above.
Change-Id: Ied84adbedc38324629c8b50178b44041f8e8b66e
diff --git a/tools/winscope/src/App.vue b/tools/winscope/src/App.vue
index 291ae7a..ab88a1b 100644
--- a/tools/winscope/src/App.vue
+++ b/tools/winscope/src/App.vue
@@ -52,6 +52,8 @@
:ref="file.type"
:store="store"
:file="file"
+ :presentTags="Object.freeze(presentTags)"
+ :presentErrors="Object.freeze(presentErrors)"
@click="onDataViewFocus(file)"
/>
</div>
@@ -102,6 +104,7 @@
simplifyNames: true,
displayDefaults: true,
navigationStyle: NAVIGATION_STYLE.GLOBAL,
+ flickerTraceView: false,
}),
overlayRef: 'overlay',
mainContentStyle: {
@@ -110,7 +113,6 @@
presentTags: [],
presentErrors: [],
searchTypes: [SEARCH_TYPE.TIMESTAMP],
- tagAndErrorTraces: false,
};
},
created() {
@@ -124,7 +126,7 @@
},
methods: {
- /** get states from either tag files or error files */
+ /** Get states from either tag files or error files */
getUpdatedStates(files) {
var states = [];
for (const file of files) {
@@ -132,7 +134,7 @@
}
return states;
},
- /** get tags from all uploaded tag files*/
+ /** Get tags from all uploaded tag files*/
getUpdatedTags() {
var tagStates = this.getUpdatedStates(this.tagFiles);
var tags = [];
@@ -144,7 +146,7 @@
});
return tags;
},
- /** get tags from all uploaded error files*/
+ /** Get tags from all uploaded error files*/
getUpdatedErrors() {
var errorStates = this.getUpdatedStates(this.errorFiles);
var errors = [];
@@ -157,11 +159,11 @@
});
return errors;
},
- /** set flicker mode check for if there are tag/error traces uploaded*/
+ /** Set flicker mode check for if there are tag/error traces uploaded*/
shouldUpdateTagAndErrorTraces() {
return this.tagFiles.length > 0 || this.errorFiles.length > 0;
},
- /** activate flicker search tab if tags/errors uploaded*/
+ /** Activate flicker search tab if tags/errors uploaded*/
updateSearchTypes() {
this.searchTypes = [SEARCH_TYPE.TIMESTAMP];
if (this.tagAndErrorTraces) this.searchTypes.push(SEARCH_TYPE.TAG);
diff --git a/tools/winscope/src/DataView.vue b/tools/winscope/src/DataView.vue
index db3c07f..7db5699 100644
--- a/tools/winscope/src/DataView.vue
+++ b/tools/winscope/src/DataView.vue
@@ -40,12 +40,16 @@
v-if="showInWindowManagerTraceView(file)"
:store="store"
:file="file"
+ :presentTags="presentTags"
+ :presentErrors="presentErrors"
ref="view"
/>
<SurfaceFlingerTraceView
v-else-if="showInSurfaceFlingerTraceView(file)"
:store="store"
:file="file"
+ :presentTags="presentTags"
+ :presentErrors="presentErrors"
ref="view"
/>
<transactionsview
@@ -151,7 +155,7 @@
this.$emit('click', e);
},
},
- props: ['store', 'file'],
+ props: ['store', 'file', 'presentTags', 'presentErrors'],
mixins: [FileType],
components: {
'traceview': TraceView,
diff --git a/tools/winscope/src/DefaultTreeElement.vue b/tools/winscope/src/DefaultTreeElement.vue
index 1df8d17..96c7dcf 100644
--- a/tools/winscope/src/DefaultTreeElement.vue
+++ b/tools/winscope/src/DefaultTreeElement.vue
@@ -43,13 +43,28 @@
{{c.long}}
</md-tooltip>
</div>
+ <div class="flicker-tags" v-for="transition in transitions" :key="transition">
+ <Arrow
+ class="transition-arrow"
+ :style="{color: transitionArrowColor(transition)}"
+ />
+ <md-tooltip md-direction="right"> {{transitionTooltip(transition)}} </md-tooltip>
+ </div>
+ <div class="flicker-tags" v-for="error in errors" :key="error.message">
+ <Arrow class="error-arrow"/>
+ <md-tooltip md-direction="right"> Error: {{error.message}} </md-tooltip>
+ </div>
</span>
</template>
<script>
+
+import Arrow from './components/TagDisplay/Arrow.vue';
+import {transitionMap} from './utils/consts.js';
+
export default {
name: 'DefaultTreeElement',
- props: ['item', 'simplify-names'],
+ props: ['item', 'simplify-names', 'errors', 'transitions'],
methods: {
chipClassForChip(c) {
return [
@@ -59,6 +74,15 @@
(c.type?.toString() || c.class?.toString() || 'default'),
];
},
+ transitionArrowColor(transition) {
+ return transitionMap.get(transition).color;
+ },
+ transitionTooltip(transition) {
+ return transitionMap.get(transition).desc;
+ },
+ },
+ components: {
+ Arrow,
},
};
</script>
@@ -100,4 +124,12 @@
flex: 1 1 auto;
width: 0;
}
+
+.flicker-tags {
+ display: inline-block;
+}
+
+.error-arrow {
+ color: red;
+}
</style>
diff --git a/tools/winscope/src/Searchbar.vue b/tools/winscope/src/Searchbar.vue
index 1f5fa4e..f893122 100644
--- a/tools/winscope/src/Searchbar.vue
+++ b/tools/winscope/src/Searchbar.vue
@@ -76,7 +76,7 @@
class="inline-error"
@click="setCurrentTimestamp(item.timestamp)"
>
- Error
+ Error: {{item.message}}
</td>
</tr>
</table>
@@ -117,17 +117,17 @@
};
},
methods: {
- /** set search type depending on tab selected */
+ /** Set search type depending on tab selected */
setSearchType(searchType) {
this.searchType = searchType;
},
- /** set tab class to determine color highlight for active tab */
+ /** Set tab class to determine color highlight for active tab */
tabClass(searchType) {
var isActive = (this.searchType === searchType) ? 'active' : 'inactive';
return ['tab', isActive];
},
- /** filter all the tags present in the trace by the searchbar input */
+ /** Filter all the tags present in the trace by the searchbar input */
filteredTags() {
var tags = [];
var filter = this.searchInput.toUpperCase();
@@ -136,7 +136,7 @@
});
return tags;
},
- /** add filtered errors to filtered tags to integrate both into table*/
+ /** Add filtered errors to filtered tags to integrate both into table*/
filteredTagsAndErrors() {
var tagsAndErrors = [...this.filteredTags()];
var filter = this.searchInput.toUpperCase();
@@ -148,9 +148,9 @@
return tagsAndErrors;
},
- /** each transition has two tags present
- * isolate the tags for the desire transition
- * add a desc to display the timestamps as strings
+ /** Each transition has two tags present
+ * Isolate the tags for the desire transition
+ * Add a desc to display the timestamps as strings
*/
transitionTags(id) {
var tags = this.filteredTags().filter((tag) => tag.id === id);
@@ -160,36 +160,36 @@
return tags;
},
- /** find the start as minimum timestamp in transition tags */
+ /** Find the start as minimum timestamp in transition tags */
transitionStart(tags) {
var times = tags.map((tag) => tag.timestamp);
return times[0];
},
- /** find the end as maximum timestamp in transition tags */
+ /** Find the end as maximum timestamp in transition tags */
transitionEnd(tags) {
var times = tags.map((tag) => tag.timestamp);
return times[times.length - 1];
},
- /** upon selecting a start/end tag in the dropdown;
+ /** Upon selecting a start/end tag in the dropdown;
* navigates to that timestamp in the timeline */
setCurrentTimestamp(timestamp) {
this.$store.dispatch("updateTimelineTime", timestamp);
},
- /** colour codes text of transition in dropdown */
+ /** Colour codes text of transition in dropdown */
transitionTextColor(transition) {
return transitionMap.get(transition).color;
},
- /** displays transition description rather than variable name */
+ /** Displays transition description rather than variable name */
transitionDesc(transition) {
return transitionMap.get(transition).desc;
},
- /** add a desc to display the error timestamps as strings */
+ /** Add a desc to display the error timestamps as strings */
errorDesc(timestamp) {
return nanos_to_string(timestamp);
},
- /** navigates to closest timestamp in timeline to search input*/
+ /** Navigates to closest timestamp in timeline to search input*/
updateSearchForTimestamp() {
if (regExpTimestampSearch.test(this.searchInput)) {
var roundedTimestamp = parseInt(this.searchInput);
diff --git a/tools/winscope/src/SurfaceFlingerTraceView.vue b/tools/winscope/src/SurfaceFlingerTraceView.vue
index b40aab1..5a848c1 100644
--- a/tools/winscope/src/SurfaceFlingerTraceView.vue
+++ b/tools/winscope/src/SurfaceFlingerTraceView.vue
@@ -14,7 +14,13 @@
-->
<template>
- <TraceView :store="store" :file="file" :summarizer="summarizer" />
+ <TraceView
+ :store="store"
+ :file="file"
+ :summarizer="summarizer"
+ :presentTags="presentTags"
+ :presentErrors="presentErrors"
+ />
</template>
<script>
@@ -22,7 +28,7 @@
export default {
name: 'SurfaceFlingerTraceView',
- props: ['store', 'file'],
+ props: ['store', 'file', 'presentTags', 'presentErrors'],
components: {
TraceView,
},
diff --git a/tools/winscope/src/TraceView.vue b/tools/winscope/src/TraceView.vue
index 6c281a5..ab77d35 100644
--- a/tools/winscope/src/TraceView.vue
+++ b/tools/winscope/src/TraceView.vue
@@ -42,6 +42,7 @@
</md-checkbox>
<md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
<md-checkbox v-model="store.flattened">Flat</md-checkbox>
+ <md-checkbox v-if="hasTagsOrErrors" v-model="store.flickerTraceView">Flicker</md-checkbox>
<md-field md-inline class="filter">
<label>Filter...</label>
<md-input v-model="hierarchyPropertyFilterString"></md-input>
@@ -56,6 +57,9 @@
:filter="hierarchyFilter"
:flattened="store.flattened"
:onlyVisible="store.onlyVisible"
+ :flickerTraceView="store.flickerTraceView"
+ :presentTags="presentTags"
+ :presentErrors="presentErrors"
:items-clickable="true"
:useGlobalCollapsedState="true"
:simplify-names="store.simplifyNames"
@@ -166,7 +170,7 @@
export default {
name: 'traceview',
- props: ['store', 'file', 'summarizer'],
+ props: ['store', 'file', 'summarizer', 'presentTags', 'presentErrors'],
data() {
return {
propertyFilterString: '',
@@ -307,10 +311,30 @@
return prevEntry;
},
+
+ /** Performs check for id match between entry and present tags/errors
+ * must be carried out for every present tag/error
+ */
+ matchItems(flickerItems, entryItem) {
+ var match = false;
+ flickerItems.forEach(flickerItem => {
+ if (flickerItem.taskId===entryItem.taskId || flickerItem.layerId===entryItem.id) {
+ match = true;
+ }
+ });
+ return match;
+ },
+ /** Returns check for id match between entry and present tags/errors */
+ isEntryTagMatch(entryItem) {
+ return this.matchItems(this.presentTags, entryItem) || this.matchItems(this.presentErrors, entryItem);
+ },
},
created() {
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
},
+ destroyed() {
+ this.store.flickerTraceView = false;
+ },
watch: {
selectedIndex() {
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
@@ -338,9 +362,12 @@
hierarchyFilter() {
const hierarchyPropertyFilter =
getFilter(this.hierarchyPropertyFilterString);
- return this.store.onlyVisible ? (c) => {
+ var fil = this.store.onlyVisible ? (c) => {
return c.isVisible && hierarchyPropertyFilter(c);
} : hierarchyPropertyFilter;
+ return this.store.flickerTraceView ? (c) => {
+ return this.isEntryTagMatch(c);
+ } : fil;
},
propertyFilter() {
return getFilter(this.propertyFilterString);
@@ -364,6 +391,9 @@
return summary;
},
+ hasTagsOrErrors() {
+ return this.presentTags.length > 0 || this.presentErrors.length > 0;
+ },
},
components: {
'tree-view': TreeView,
diff --git a/tools/winscope/src/TreeView.vue b/tools/winscope/src/TreeView.vue
index ff3c04f..846924e 100644
--- a/tools/winscope/src/TreeView.vue
+++ b/tools/winscope/src/TreeView.vue
@@ -50,7 +50,12 @@
/>
</div>
<div v-else>
- <DefaultTreeElement :item="item" :simplify-names="simplifyNames"/>
+ <DefaultTreeElement
+ :item="item"
+ :simplify-names="simplifyNames"
+ :errors="errors"
+ :transitions="transitions"
+ />
</div>
</div>
<div v-show="isCollapsed">
@@ -89,6 +94,9 @@
:flattened="flattened"
:onlyVisible="onlyVisible"
:simplify-names="simplifyNames"
+ :flickerTraceView="flickerTraceView"
+ :presentTags="currentTags"
+ :presentErrors="currentErrors"
:force-flattened="applyingFlattened"
v-show="filterMatches(c)"
:items-clickable="itemsClickable"
@@ -113,7 +121,6 @@
<script>
import DefaultTreeElement from './DefaultTreeElement.vue';
import NodeContextMenu from './NodeContextMenu.vue';
-
import {DiffType} from './utils/diff.js';
/* in px, must be kept in sync with css, maybe find a better solution... */
@@ -143,6 +150,9 @@
// Custom view to use to render the elements in the tree view
'elementView',
'onlyVisible',
+ 'flickerTraceView',
+ 'presentTags',
+ 'presentErrors',
],
data() {
const isCollapsedByDefault = this.collapse ?? false;
@@ -163,6 +173,10 @@
[DiffType.MODIFIED]: '.',
[DiffType.MOVED]: '.',
},
+ currentTags: [],
+ currentErrors: [],
+ transitions: [],
+ errors: [],
};
},
watch: {
@@ -179,6 +193,10 @@
},
currentTimestamp() {
// Update anything that is required to change when time changes.
+ this.currentTags = this.getCurrentItems(this.presentTags);
+ this.currentErrors = this.getCurrentItems(this.presentErrors);
+ this.transitions = this.getCurrentTransitions();
+ this.errors = this.getCurrentErrorTags();
this.updateCollapsedDiffClass();
},
isSelected(isSelected) {
@@ -426,7 +444,48 @@
marginTop: '0px',
}
}
- }
+ },
+
+ /** Check if tag/error id matches entry id */
+ isIdMatch(a, b) {
+ return a.taskId===b.taskId || a.layerId===b.id;
+ },
+ /** Performs check for id match between entry and present tags/errors
+ * exits once match has been found
+ */
+ matchItems(flickerItems) {
+ var match = false;
+ flickerItems.every(flickerItem => {
+ if (this.isIdMatch(flickerItem, this.item)) {
+ match = true;
+ return false;
+ }
+ });
+ return match;
+ },
+ /** Returns check for id match between entry and present tags/errors */
+ isEntryTagMatch() {
+ return this.matchItems(this.currentTags) || this.matchItems(this.currentErrors);
+ },
+
+ getCurrentItems(items) {
+ if (!items) return [];
+ else return items.filter(item => item.timestamp===this.currentTimestamp);
+ },
+ getCurrentTransitions() {
+ var transitions = [];
+ var ids = [];
+ this.currentTags.forEach(tag => {
+ if (!ids.includes(tag.id) && this.isIdMatch(tag, this.item)) {
+ transitions.push(tag.transition);
+ ids.push(tag.id);
+ }
+ });
+ return transitions;
+ },
+ getCurrentErrorTags() {
+ return this.currentErrors.filter(error => this.isIdMatch(error, this.item));
+ },
},
computed: {
hasDiff() {
@@ -486,7 +545,12 @@
const offset = levelOffset * (this.depth + this.isLeaf) + 'px';
var display = "";
- if (this.onlyVisible && !this.item.isVisible) display = 'none';
+ if (!this.item.timestamp
+ && this.flattened
+ && (this.onlyVisible && !this.item.isVisible ||
+ this.flickerTraceView && !this.isEntryTagMatch())) {
+ display = 'none';
+ }
return {
marginLeft: '-' + offset,
diff --git a/tools/winscope/src/WindowManagerTraceView.vue b/tools/winscope/src/WindowManagerTraceView.vue
index e24426c..d2d709a 100644
--- a/tools/winscope/src/WindowManagerTraceView.vue
+++ b/tools/winscope/src/WindowManagerTraceView.vue
@@ -14,7 +14,13 @@
-->
<template>
- <TraceView :store="store" :file="file" :summarizer="summarizer" />
+ <TraceView
+ :store="store"
+ :file="file"
+ :summarizer="summarizer"
+ :presentTags="presentTags"
+ :presentErrors="presentErrors"
+ />
</template>
<script>
@@ -22,7 +28,7 @@
export default {
name: "WindowManagerTraceView",
- props: ["store", "file"],
+ props: ["store", "file", "presentTags", "presentErrors"],
components: {
TraceView
},
diff --git a/tools/winscope/src/components/TagDisplay/Arrow.vue b/tools/winscope/src/components/TagDisplay/Arrow.vue
index 8059de4..0a19705 100644
--- a/tools/winscope/src/components/TagDisplay/Arrow.vue
+++ b/tools/winscope/src/components/TagDisplay/Arrow.vue
@@ -24,7 +24,6 @@
<style scoped>
.arrow {
display: inline-block;
- position: absolute;
width: 0;
height: 0;
border-left: 6px solid transparent;
diff --git a/tools/winscope/src/components/TagDisplay/TransitionContainer.vue b/tools/winscope/src/components/TagDisplay/TransitionContainer.vue
index 701cbad..0c74c66 100644
--- a/tools/winscope/src/components/TagDisplay/TransitionContainer.vue
+++ b/tools/winscope/src/components/TagDisplay/TransitionContainer.vue
@@ -92,10 +92,12 @@
}
.arrow-start {
+ position: absolute;
left: 0%;
}
.arrow-end {
+ position: absolute;
right: 0%;
}