blob: 8fd2fcbf870210974dcbfe55c8282b9e745207c6 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/trace_stream.html">
<link rel="import" href="/tracing/importer/importer.html">
<link rel="import" href="/tracing/model/model.html">
<script>
'use strict';
tr.exportTo('tr.e.importer.fuchsia', function() {
const IMPORT_PRIORITY = 0;
const IDLE_THREAD_THRESHOLD = 6444000000;
// Zircon thread state constants from:
// https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/object_get_info.md
const ZX_THREAD_STATE_NEW = 0;
const ZX_THREAD_STATE_RUNNING = 1;
const ZX_THREAD_STATE_SUSPENDED = 2;
const ZX_THREAD_STATE_BLOCKED = 3;
const ZX_THREAD_STATE_DYING = 4;
const ZX_THREAD_STATE_DEAD = 5;
class FuchsiaImporter extends tr.importer.Importer {
constructor(model, eventData) {
super(model, eventData);
this.importPriority = IMPORT_PRIORITY;
this.model_ = model;
this.events_ = eventData.events;
this.parsers_ = [];
this.threadInfo_ = new Map();
this.processNames_ = new Map();
this.threadStates_ = new Map();
}
static canImport(eventData) {
if (eventData instanceof tr.b.TraceStream) {
if (eventData.isBinary) return false;
eventData = eventData.header;
}
if (eventData instanceof Object && eventData.type === 'fuchsia') {
return true;
}
return false;
}
get importerName() {
return 'FuchsiaImporter';
}
get model() {
return this.model_;
}
importClockSyncMarkers() {
}
finalizeImport() {
}
isIdleThread(prio, tid) {
if (prio === undefined) {
// If the "prio" field is not available (if we were, for example,
// using an old trace), then fall back to the legacy heuristic of
// assuming that large numbered threads are idle ones.
return tid > IDLE_THREAD_THRESHOLD;
}
// A thread is idle iff its priority is set to 0.
return prio === 0;
}
recordThreadState_(tid, timestamp, state, prio) {
if (this.isIdleThread(prio, tid)) {
return;
}
const states =
this.threadStates_.has(tid) ? this.threadStates_.get(tid) : [];
states.push({'ts': timestamp, state});
this.threadStates_.set(tid, states);
}
// Context switch events take the form:
// {
// "ph": "k",
// "ts": 151981130.88743783,
// "cpu": 1,
// "out": {
// "pid": 25977,
// "tid": 28909,
// "state": 3,
// "prio": 20
// },
// "in": {
// "pid": 0,
// "tid": 6444931392,
// "prio": 0
// }
// },
processContextSwitchEvent_(event) {
let tid = event.in.tid;
let threadName = tid.toString();
let procName = '';
const prio = event.in.prio;
if (this.threadInfo_.has(tid)) {
const threadInfo = this.threadInfo_.get(tid);
threadName = threadInfo.name;
const pid = threadInfo.pid;
if (this.processNames_.has(pid)) {
procName = this.processNames_.get(pid) + ':';
}
}
const name = procName + threadName;
if (this.isIdleThread(prio, tid)) {
tid = undefined; // Fake kernel idle task
}
const cpu = this.model_.kernel.getOrCreateCpu(event.cpu);
const timestamp = tr.b.Unit.timestampFromUs(event.ts);
cpu.switchActiveThread(timestamp, {}, tid, name, tid);
const SCHEDULING_STATE = tr.model.SCHEDULING_STATE;
this.recordThreadState_(tid, timestamp, SCHEDULING_STATE.RUNNING, prio);
let outState = SCHEDULING_STATE.UNKNOWN;
switch (event.out.state) {
case ZX_THREAD_STATE_NEW:
outState = SCHEDULING_STATE.RUNNABLE;
break;
case ZX_THREAD_STATE_RUNNING:
outState = SCHEDULING_STATE.RUNNABLE;
break;
case ZX_THREAD_STATE_BLOCKED:
outState = SCHEDULING_STATE.SLEEPING;
break;
case ZX_THREAD_STATE_SUSPENDED:
outState = SCHEDULING_STATE.STOPPED;
break;
case ZX_THREAD_STATE_DEAD:
outState = SCHEDULING_STATE.TASK_DEAD;
break;
}
this.recordThreadState_(event.out.tid, timestamp, outState,
event.out.prio);
}
processProcessInfoEvent_(event) {
const process = this.model_.getOrCreateProcess(event.pid);
process.name = event.name;
this.processNames_.set(event.pid, event.name);
if ('sort_index' in event) {
process.sortIndex = event.sort_index;
}
}
processThreadInfoEvent_(event) {
const thread = this.model_.getOrCreateProcess(event.pid).
getOrCreateThread(event.tid);
thread.name = event.name;
this.threadInfo_.set(event.tid, {'name': event.name, 'pid': event.pid});
if ('sort_index' in event) {
const thread = this.model_.getOrCreateProcess(event.pid).
getOrCreateThread(event.tid);
thread.sortIndex = event.sort_index;
}
}
processEvent_(event) {
switch (event.ph) {
case 'k':
this.processContextSwitchEvent_(event);
break;
case 'p':
this.processProcessInfoEvent_(event);
break;
case 't':
this.processThreadInfoEvent_(event);
break;
}
}
postProcessStates_() {
for (const [tid, states] of this.threadStates_) {
if (!this.threadInfo_.has(tid)) {
continue;
}
const pid = this.threadInfo_.get(tid).pid;
const thread = this.model_.getOrCreateProcess(
pid).getOrCreateThread(tid);
const slices = [];
for (let i = 0; i < states.length - 1; i++) {
slices.push(new tr.model.ThreadTimeSlice(
thread, states[i].state, '',
states[i].ts, {}, states[i + 1].ts - states[i].ts));
}
thread.timeSlices = slices;
}
}
/**
* Imports the data in this.events_ into model_.
*/
importEvents() {
for (const event of this.events_) {
this.processEvent_(event);
}
this.postProcessStates_();
}
}
tr.importer.Importer.register(FuchsiaImporter);
return {
FuchsiaImporter,
IMPORT_PRIORITY,
};
});
</script>