Merge "trace_processor: add all bits iterator"
diff --git a/BUILD b/BUILD
index 04c6763..74e1390 100644
--- a/BUILD
+++ b/BUILD
@@ -728,6 +728,7 @@
"src/trace_processor/systrace_trace_parser.h",
"src/trace_processor/thread_table.cc",
"src/trace_processor/thread_table.h",
+ "src/trace_processor/timestamped_trace_piece.h",
"src/trace_processor/trace_blob_view.h",
"src/trace_processor/trace_parser.h",
"src/trace_processor/trace_processor.cc",
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 0d73d56..9302662 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -3005,11 +3005,27 @@
// Begin of protos/perfetto/trace/profiling/heap_graph.proto
message HeapGraphRoot {
+ enum Type {
+ ROOT_UNKNOWN = 0;
+ ROOT_JNI_GLOBAL = 1;
+ ROOT_JNI_LOCAL = 2;
+ ROOT_JAVA_FRAME = 3;
+ ROOT_NATIVE_STACK = 4;
+ ROOT_STICKY_CLASS = 5;
+ ROOT_THREAD_BLOCK = 6;
+ ROOT_MONITOR_USED = 7;
+ ROOT_THREAD_OBJECT = 8;
+ ROOT_INTERNED_STRING = 9;
+ ROOT_FINALIZING = 10;
+ ROOT_DEBUGGER = 11;
+ ROOT_REFERENCE_CLEANUP = 12;
+ ROOT_VM_INTERNAL = 13;
+ ROOT_JNI_MONITOR = 14;
+ };
// Objects retained by this root.
repeated uint64 object_ids = 1;
- // From art:RootType, e.g. "kRootThreadObject".
- optional string root_type = 2;
+ optional Type root_type = 2;
}
message HeapGraphObject {
diff --git a/protos/perfetto/trace/profiling/heap_graph.proto b/protos/perfetto/trace/profiling/heap_graph.proto
index f037545..d29c292 100644
--- a/protos/perfetto/trace/profiling/heap_graph.proto
+++ b/protos/perfetto/trace/profiling/heap_graph.proto
@@ -25,11 +25,27 @@
package perfetto.protos;
message HeapGraphRoot {
+ enum Type {
+ ROOT_UNKNOWN = 0;
+ ROOT_JNI_GLOBAL = 1;
+ ROOT_JNI_LOCAL = 2;
+ ROOT_JAVA_FRAME = 3;
+ ROOT_NATIVE_STACK = 4;
+ ROOT_STICKY_CLASS = 5;
+ ROOT_THREAD_BLOCK = 6;
+ ROOT_MONITOR_USED = 7;
+ ROOT_THREAD_OBJECT = 8;
+ ROOT_INTERNED_STRING = 9;
+ ROOT_FINALIZING = 10;
+ ROOT_DEBUGGER = 11;
+ ROOT_REFERENCE_CLEANUP = 12;
+ ROOT_VM_INTERNAL = 13;
+ ROOT_JNI_MONITOR = 14;
+ };
// Objects retained by this root.
repeated uint64 object_ids = 1;
- // From art:RootType, e.g. "kRootThreadObject".
- optional string root_type = 2;
+ optional Type root_type = 2;
}
message HeapGraphObject {
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index fe3ce2a..c99e665 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -151,6 +151,7 @@
"systrace_trace_parser.h",
"thread_table.cc",
"thread_table.h",
+ "timestamped_trace_piece.h",
"trace_blob_view.h",
"trace_parser.h",
"trace_processor.cc",
diff --git a/src/trace_processor/event_tracker.cc b/src/trace_processor/event_tracker.cc
index 79757a9..2886c3a 100644
--- a/src/trace_processor/event_tracker.cc
+++ b/src/trace_processor/event_tracker.cc
@@ -240,7 +240,7 @@
defn_id = definitions->AddCounterDefinition(name_id, ref, ref_type);
}
RowId row_id = PushCounter(timestamp, value, defn_id);
- if (resolve_utid_to_upid) {
+ if (resolve_utid_to_upid && row_id != kInvalidRowId) {
auto table_and_row = TraceStorage::ParseRowId(row_id);
PendingUpidResolutionCounter pending;
pending.row = table_and_row.second;
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index 5f6a99f..765b0a5 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -24,6 +24,7 @@
#include "src/trace_processor/proto_trace_parser.h"
#include "src/trace_processor/proto_trace_tokenizer.h"
#include "src/trace_processor/systrace_trace_parser.h"
+#include "src/trace_processor/trace_sorter.h"
// JSON parsing and exporting is only supported in the standalone and
// Chromium builds.
diff --git a/src/trace_processor/fuchsia_trace_parser.cc b/src/trace_processor/fuchsia_trace_parser.cc
index 8e9a50a..3423db9 100644
--- a/src/trace_processor/fuchsia_trace_parser.cc
+++ b/src/trace_processor/fuchsia_trace_parser.cc
@@ -63,24 +63,25 @@
void FuchsiaTraceParser::ParseFtracePacket(uint32_t,
int64_t,
- TraceSorter::TimestampedTracePiece) {
+ TimestampedTracePiece) {
PERFETTO_FATAL("Fuchsia Trace Parser cannot handle ftrace packets.");
}
-void FuchsiaTraceParser::ParseTracePacket(
- int64_t,
- TraceSorter::TimestampedTracePiece ttp) {
+void FuchsiaTraceParser::ParseTracePacket(int64_t, TimestampedTracePiece ttp) {
PERFETTO_DCHECK(ttp.fuchsia_provider_view != nullptr);
// The timestamp is also present in the record, so we'll ignore the one passed
// as an argument.
- const uint64_t* current =
- reinterpret_cast<const uint64_t*>(ttp.blob_view.data());
+ fuchsia_trace_utils::RecordCursor cursor(&ttp.blob_view);
FuchsiaProviderView* provider_view = ttp.fuchsia_provider_view.get();
ProcessTracker* procs = context_->process_tracker.get();
SliceTracker* slices = context_->slice_tracker.get();
- uint64_t header = *current++;
+ uint64_t header;
+ if (!cursor.ReadUint64(&header)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
uint32_t record_type = fuchsia_trace_utils::ReadField<uint32_t>(header, 0, 3);
switch (record_type) {
case kEvent: {
@@ -95,25 +96,39 @@
uint32_t name_ref =
fuchsia_trace_utils::ReadField<uint32_t>(header, 48, 63);
- int64_t ts = fuchsia_trace_utils::ReadTimestamp(
- ¤t, provider_view->get_ticks_per_second());
+ int64_t ts;
+ if (!cursor.ReadTimestamp(provider_view->get_ticks_per_second(), &ts)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
fuchsia_trace_utils::ThreadInfo tinfo;
if (fuchsia_trace_utils::IsInlineThread(thread_ref)) {
- tinfo = fuchsia_trace_utils::ReadInlineThread(¤t);
+ if (!cursor.ReadInlineThread(&tinfo)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
} else {
tinfo = provider_view->GetThread(thread_ref);
}
StringId cat;
if (fuchsia_trace_utils::IsInlineString(cat_ref)) {
- cat = context_->storage->InternString(
- fuchsia_trace_utils::ReadInlineString(¤t, cat_ref));
+ base::StringView cat_string_view;
+ if (!cursor.ReadInlineString(cat_ref, &cat_string_view)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ cat = context_->storage->InternString(cat_string_view);
} else {
cat = provider_view->GetString(cat_ref);
}
StringId name;
if (fuchsia_trace_utils::IsInlineString(name_ref)) {
- name = context_->storage->InternString(
- fuchsia_trace_utils::ReadInlineString(¤t, name_ref));
+ base::StringView name_string_view;
+ if (!cursor.ReadInlineString(name_ref, &name_string_view)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ name = context_->storage->InternString(name_string_view);
} else {
name = provider_view->GetString(name_ref);
}
@@ -121,8 +136,12 @@
// Read arguments
std::vector<Arg> args;
for (uint32_t i = 0; i < n_args; i++) {
- const uint64_t* arg_base = current;
- uint64_t arg_header = *current++;
+ size_t arg_base = cursor.WordIndex();
+ uint64_t arg_header;
+ if (!cursor.ReadUint64(&arg_header)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
uint32_t arg_type =
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 0, 3);
uint32_t arg_size_words =
@@ -131,8 +150,12 @@
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 16, 31);
Arg arg;
if (fuchsia_trace_utils::IsInlineString(arg_name_ref)) {
- arg.name = context_->storage->InternString(
- fuchsia_trace_utils::ReadInlineString(¤t, arg_name_ref));
+ base::StringView arg_name_view;
+ if (!cursor.ReadInlineString(arg_name_ref, &arg_name_view)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ arg.name = context_->storage->InternString(arg_name_view);
} else {
arg.name = provider_view->GetString(arg_name_ref);
}
@@ -149,17 +172,30 @@
arg.value = fuchsia_trace_utils::ArgValue::Uint32(
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 63));
break;
- case kInt64:
- arg.value = fuchsia_trace_utils::ArgValue::Int64(
- static_cast<int64_t>(*current++));
+ case kInt64: {
+ int64_t value;
+ if (!cursor.ReadInt64(&value)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ arg.value = fuchsia_trace_utils::ArgValue::Int64(value);
break;
- case kUint64:
- arg.value = fuchsia_trace_utils::ArgValue::Uint64(*current++);
+ }
+ case kUint64: {
+ uint64_t value;
+ if (!cursor.ReadUint64(&value)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ arg.value = fuchsia_trace_utils::ArgValue::Uint64(value);
break;
+ }
case kDouble: {
double value;
- memcpy(&value, current, sizeof(double));
- current++;
+ if (!cursor.ReadDouble(&value)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
arg.value = fuchsia_trace_utils::ArgValue::Double(value);
break;
}
@@ -168,28 +204,43 @@
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 47);
StringId value;
if (fuchsia_trace_utils::IsInlineString(arg_value_ref)) {
- value = context_->storage->InternString(
- fuchsia_trace_utils::ReadInlineString(¤t,
- arg_value_ref));
+ base::StringView arg_value_view;
+ if (!cursor.ReadInlineString(arg_value_ref, &arg_value_view)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ value = context_->storage->InternString(arg_value_view);
} else {
value = provider_view->GetString(arg_value_ref);
}
arg.value = fuchsia_trace_utils::ArgValue::String(value);
break;
}
- case kPointer:
- arg.value = fuchsia_trace_utils::ArgValue::Pointer(*current++);
+ case kPointer: {
+ uint64_t value;
+ if (!cursor.ReadUint64(&value)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ arg.value = fuchsia_trace_utils::ArgValue::Pointer(value);
break;
- case kKoid:
- arg.value = fuchsia_trace_utils::ArgValue::Koid(*current++);
+ }
+ case kKoid: {
+ uint64_t value;
+ if (!cursor.ReadUint64(&value)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ arg.value = fuchsia_trace_utils::ArgValue::Koid(value);
break;
+ }
default:
arg.value = fuchsia_trace_utils::ArgValue::Unknown();
break;
}
args.push_back(arg);
- current = arg_base + arg_size_words;
+ cursor.SetWordIndex(arg_base + arg_size_words);
}
switch (event_type) {
@@ -282,12 +333,16 @@
break;
}
case kDurationComplete: {
- int64_t end_ts = fuchsia_trace_utils::ReadTimestamp(
- ¤t, provider_view->get_ticks_per_second());
+ int64_t end_ts;
+ if (!cursor.ReadTimestamp(provider_view->get_ticks_per_second(),
+ &end_ts)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
int64_t duration = end_ts - ts;
if (duration < 0) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
- break;
+ return;
}
UniqueTid utid =
procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
@@ -298,7 +353,11 @@
break;
}
case kAsyncBegin: {
- int64_t correlation_id = static_cast<int64_t>(*current++);
+ int64_t correlation_id;
+ if (!cursor.ReadInt64(&correlation_id)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
TrackId track_id = context_->track_tracker->InternFuchsiaAsyncTrack(
name, correlation_id);
slices->Begin(ts, track_id, track_id, RefType::kRefTrack, cat, name);
@@ -308,7 +367,11 @@
// TODO(eseckler): Consider storing these instants as 0-duration
// slices instead, so that they get nested underneath begin/end
// slices.
- int64_t correlation_id = static_cast<int64_t>(*current++);
+ int64_t correlation_id;
+ if (!cursor.ReadInt64(&correlation_id)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
TrackId track_id = context_->track_tracker->InternFuchsiaAsyncTrack(
name, correlation_id);
RowId row = context_->event_tracker->PushInstant(
@@ -322,7 +385,11 @@
break;
}
case kAsyncEnd: {
- int64_t correlation_id = static_cast<int64_t>(*current++);
+ int64_t correlation_id;
+ if (!cursor.ReadInt64(&correlation_id)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
TrackId track_id = context_->track_tracker->InternFuchsiaAsyncTrack(
name, correlation_id);
slices->End(ts, track_id, cat, name);
diff --git a/src/trace_processor/fuchsia_trace_parser.h b/src/trace_processor/fuchsia_trace_parser.h
index 67cc95b..8992ce6 100644
--- a/src/trace_processor/fuchsia_trace_parser.h
+++ b/src/trace_processor/fuchsia_trace_parser.h
@@ -18,6 +18,7 @@
#define SRC_TRACE_PROCESSOR_FUCHSIA_TRACE_PARSER_H_
#include "src/trace_processor/fuchsia_provider_view.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
#include "src/trace_processor/trace_parser.h"
namespace perfetto {
@@ -31,11 +32,8 @@
~FuchsiaTraceParser() override;
// TraceParser implementation
- void ParseTracePacket(int64_t timestamp,
- TraceSorter::TimestampedTracePiece) override;
- void ParseFtracePacket(uint32_t,
- int64_t,
- TraceSorter::TimestampedTracePiece) override;
+ void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) override;
+ void ParseFtracePacket(uint32_t, int64_t, TimestampedTracePiece) override;
private:
TraceProcessorContext* const context_;
diff --git a/src/trace_processor/fuchsia_trace_tokenizer.cc b/src/trace_processor/fuchsia_trace_tokenizer.cc
index c056d1e..5f9a4b8 100644
--- a/src/trace_processor/fuchsia_trace_tokenizer.cc
+++ b/src/trace_processor/fuchsia_trace_tokenizer.cc
@@ -187,8 +187,12 @@
ProcessTracker* procs = context_->process_tracker.get();
TraceSorter* sorter = context_->sorter.get();
- const uint64_t* record = reinterpret_cast<const uint64_t*>(tbv.data());
- uint64_t header = *record;
+ fuchsia_trace_utils::RecordCursor cursor(&tbv);
+ uint64_t header;
+ if (!cursor.ReadUint64(&header)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
uint32_t record_type = fuchsia_trace_utils::ReadField<uint32_t>(header, 0, 3);
@@ -208,8 +212,12 @@
fuchsia_trace_utils::ReadField<uint32_t>(header, 20, 51);
uint32_t name_len =
fuchsia_trace_utils::ReadField<uint32_t>(header, 52, 59);
- std::string name(reinterpret_cast<const char*>(&record[1]), name_len);
- RegisterProvider(provider_id, name);
+ base::StringView name_view;
+ if (!cursor.ReadInlineString(name_len, &name_view)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ RegisterProvider(provider_id, name_view.ToStdString());
break;
}
case kProviderSection: {
@@ -228,14 +236,21 @@
break;
}
case kInitialization: {
- current_provider_->ticks_per_second = record[1];
+ if (!cursor.ReadUint64(¤t_provider_->ticks_per_second)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
break;
}
case kString: {
uint32_t index = fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 30);
if (index != 0) {
uint32_t len = fuchsia_trace_utils::ReadField<uint32_t>(header, 32, 46);
- base::StringView s(reinterpret_cast<const char*>(&record[1]), len);
+ base::StringView s;
+ if (!cursor.ReadInlineString(len, &s)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
StringId id = storage->InternString(s);
current_provider_->string_table[index] = id;
@@ -246,8 +261,10 @@
uint32_t index = fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 23);
if (index != 0) {
fuchsia_trace_utils::ThreadInfo tinfo;
- tinfo.pid = record[1];
- tinfo.tid = record[2];
+ if (!cursor.ReadInlineThread(&tinfo)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
current_provider_->thread_table[index] = tinfo;
}
@@ -265,22 +282,25 @@
// the record. This means the thread information if not inline, and any
// non-inline strings (name, category for now, arg names and string values
// in the future.
- const uint64_t* current = &record[1];
auto provider_view =
std::unique_ptr<FuchsiaProviderView>(new FuchsiaProviderView());
provider_view->set_ticks_per_second(current_provider_->ticks_per_second);
- uint64_t ticks = *current++;
+ uint64_t ticks;
+ if (!cursor.ReadUint64(&ticks)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
int64_t ts = fuchsia_trace_utils::TicksToNs(
ticks, current_provider_->ticks_per_second);
if (ts < 0) {
storage->IncrementStats(stats::fuchsia_timestamp_overflow);
- break;
+ return;
}
if (fuchsia_trace_utils::IsInlineThread(thread_ref)) {
// Skip over inline thread
- fuchsia_trace_utils::ReadInlineThread(¤t);
+ cursor.ReadInlineThread(nullptr);
} else {
provider_view->InsertThread(
thread_ref, current_provider_->thread_table[thread_ref]);
@@ -288,7 +308,7 @@
if (fuchsia_trace_utils::IsInlineString(cat_ref)) {
// Skip over inline string
- fuchsia_trace_utils::ReadInlineString(¤t, cat_ref);
+ cursor.ReadInlineString(cat_ref, nullptr);
} else {
provider_view->InsertString(cat_ref,
current_provider_->string_table[cat_ref]);
@@ -296,7 +316,7 @@
if (fuchsia_trace_utils::IsInlineString(name_ref)) {
// Skip over inline string
- fuchsia_trace_utils::ReadInlineString(¤t, name_ref);
+ cursor.ReadInlineString(name_ref, nullptr);
} else {
provider_view->InsertString(name_ref,
current_provider_->string_table[name_ref]);
@@ -305,8 +325,12 @@
uint32_t n_args =
fuchsia_trace_utils::ReadField<uint32_t>(header, 20, 23);
for (uint32_t i = 0; i < n_args; i++) {
- const uint64_t* arg_base = current;
- uint64_t arg_header = *current++;
+ const size_t arg_base = cursor.WordIndex();
+ uint64_t arg_header;
+ if (!cursor.ReadUint64(&arg_header)) {
+ storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
uint32_t arg_type =
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 0, 3);
uint32_t arg_size_words =
@@ -316,7 +340,7 @@
if (fuchsia_trace_utils::IsInlineString(arg_name_ref)) {
// Skip over inline string
- fuchsia_trace_utils::ReadInlineString(¤t, arg_name_ref);
+ cursor.ReadInlineString(arg_name_ref, nullptr);
} else {
provider_view->InsertString(
arg_name_ref, current_provider_->string_table[arg_name_ref]);
@@ -327,14 +351,14 @@
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 47);
if (fuchsia_trace_utils::IsInlineString(arg_value_ref)) {
// Skip over inline string
- fuchsia_trace_utils::ReadInlineString(¤t, arg_value_ref);
+ cursor.ReadInlineString(arg_value_ref, nullptr);
} else {
provider_view->InsertString(
arg_value_ref, current_provider_->string_table[arg_value_ref]);
}
}
- current = arg_base + arg_size_words;
+ cursor.SetWordIndex(arg_base + arg_size_words);
}
sorter->PushFuchsiaRecord(ts, std::move(tbv), std::move(provider_view));
@@ -347,13 +371,20 @@
uint32_t name_ref =
fuchsia_trace_utils::ReadField<uint32_t>(header, 24, 39);
- const uint64_t* current = &record[1];
- uint64_t obj_id = *current++;
+ uint64_t obj_id;
+ if (!cursor.ReadUint64(&obj_id)) {
+ storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
StringId name = StringId();
if (fuchsia_trace_utils::IsInlineString(name_ref)) {
- name = storage->InternString(
- fuchsia_trace_utils::ReadInlineString(¤t, name_ref));
+ base::StringView name_view;
+ if (!cursor.ReadInlineString(name_ref, &name_view)) {
+ storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
+ name = storage->InternString(name_view);
} else {
name = current_provider_->string_table[name_ref];
}
@@ -376,8 +407,12 @@
// Scan for a Kernel Object argument named "process"
for (uint32_t i = 0; i < n_args; i++) {
- const uint64_t* arg_base = current;
- uint64_t arg_header = *current++;
+ const size_t arg_base = cursor.WordIndex();
+ uint64_t arg_header;
+ if (!cursor.ReadUint64(&arg_header)) {
+ storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
uint32_t arg_type =
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 0, 3);
uint32_t arg_size =
@@ -387,19 +422,24 @@
fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 16, 31);
base::StringView arg_name;
if (fuchsia_trace_utils::IsInlineString(arg_name_ref)) {
- arg_name = fuchsia_trace_utils::ReadInlineString(¤t,
- arg_name_ref);
+ if (!cursor.ReadInlineString(arg_name_ref, &arg_name)) {
+ storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
} else {
arg_name = storage->GetString(
current_provider_->string_table[arg_name_ref]);
}
if (arg_name == "process") {
- pid = *current++;
+ if (!cursor.ReadUint64(&pid)) {
+ storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
}
}
- current = arg_base + arg_size;
+ cursor.SetWordIndex(arg_base + arg_size);
}
pid_table_[obj_id] = pid;
@@ -429,26 +469,32 @@
int32_t outgoing_priority =
fuchsia_trace_utils::ReadField<int32_t>(header, 44, 51);
- uint64_t ticks = record[1];
- int64_t ts = fuchsia_trace_utils::TicksToNs(
- ticks, current_provider_->ticks_per_second);
+ int64_t ts;
+ if (!cursor.ReadTimestamp(current_provider_->ticks_per_second, &ts)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
if (ts == -1) {
context_->storage->IncrementStats(stats::fuchsia_invalid_event);
- break;
+ return;
}
- const uint64_t* current = &record[2];
-
fuchsia_trace_utils::ThreadInfo outgoing_thread;
if (fuchsia_trace_utils::IsInlineThread(outgoing_thread_ref)) {
- outgoing_thread = fuchsia_trace_utils::ReadInlineThread(¤t);
+ if (!cursor.ReadInlineThread(&outgoing_thread)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
} else {
outgoing_thread = current_provider_->thread_table[outgoing_thread_ref];
}
fuchsia_trace_utils::ThreadInfo incoming_thread;
if (fuchsia_trace_utils::IsInlineThread(incoming_thread_ref)) {
- incoming_thread = fuchsia_trace_utils::ReadInlineThread(¤t);
+ if (!cursor.ReadInlineThread(&incoming_thread)) {
+ context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+ return;
+ }
} else {
incoming_thread = current_provider_->thread_table[incoming_thread_ref];
}
diff --git a/src/trace_processor/fuchsia_trace_utils.cc b/src/trace_processor/fuchsia_trace_utils.cc
index 6d87731..cca1997 100644
--- a/src/trace_processor/fuchsia_trace_utils.cc
+++ b/src/trace_processor/fuchsia_trace_utils.cc
@@ -31,35 +31,10 @@
return (string_ref & kInlineStringMarker) || (string_ref == 0);
}
-base::StringView ReadInlineString(const uint64_t** current_ptr,
- uint32_t string_ref) {
- // Note that this works correctly for the empty string, where string_ref is 0.
- size_t len = string_ref & kInlineStringLengthMask;
- size_t len_words = (len + 7) / 8;
- base::StringView s(reinterpret_cast<const char*>(*current_ptr), len);
- *current_ptr += len_words;
- return s;
-}
-
bool IsInlineThread(uint32_t thread_ref) {
return thread_ref == 0;
}
-ThreadInfo ReadInlineThread(const uint64_t** current_ptr) {
- ThreadInfo ret;
- ret.pid = **current_ptr;
- (*current_ptr)++;
- ret.tid = **current_ptr;
- (*current_ptr)++;
- return ret;
-}
-
-int64_t ReadTimestamp(const uint64_t** current_ptr, uint64_t ticks_per_second) {
- uint64_t ticks = **current_ptr;
- (*current_ptr)++;
- return TicksToNs(ticks, ticks_per_second);
-}
-
// Converts a tick count to nanoseconds. Returns -1 if the result would not
// fit in a nonnegative int64_t. Negative timestamps are not allowed by the
// Fuchsia trace format. Also returns -1 if ticks_per_second is zero.
@@ -114,6 +89,107 @@
PERFETTO_FATAL("Not reached"); // Make GCC happy.
}
+size_t RecordCursor::WordIndex() {
+ return word_index_;
+}
+
+void RecordCursor::SetWordIndex(size_t index) {
+ word_index_ = index;
+}
+
+bool RecordCursor::ReadTimestamp(uint64_t ticks_per_second, int64_t* ts_out) {
+ const uint64_t* ts_data;
+ if (!ReadWords(1, &ts_data)) {
+ return false;
+ }
+ if (ts_out != nullptr) {
+ *ts_out = TicksToNs(*ts_data, ticks_per_second);
+ }
+ return true;
+}
+
+bool RecordCursor::ReadInlineString(uint32_t string_ref_or_len,
+ base::StringView* string_out) {
+ // Note that this works correctly for the empty string, where string_ref is 0.
+ size_t len = string_ref_or_len & kInlineStringLengthMask;
+ size_t len_words = (len + 7) / 8;
+ const uint64_t* string_data;
+ if (!ReadWords(len_words, &string_data)) {
+ return false;
+ }
+ if (string_out != nullptr) {
+ *string_out =
+ base::StringView(reinterpret_cast<const char*>(string_data), len);
+ }
+ return true;
+}
+
+bool RecordCursor::ReadInlineThread(ThreadInfo* thread_out) {
+ const uint64_t* thread_data;
+ if (!ReadWords(2, &thread_data)) {
+ return false;
+ }
+ if (thread_out != nullptr) {
+ thread_out->pid = thread_data[0];
+ thread_out->tid = thread_data[1];
+ }
+ return true;
+}
+
+bool RecordCursor::ReadInt64(int64_t* out) {
+ const uint64_t* out_data;
+ if (!ReadWords(1, &out_data)) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = static_cast<int64_t>(*out_data);
+ }
+ return true;
+}
+
+bool RecordCursor::ReadUint64(uint64_t* out) {
+ const uint64_t* out_data;
+ if (!ReadWords(1, &out_data)) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = *out_data;
+ }
+ return true;
+}
+
+bool RecordCursor::ReadDouble(double* out) {
+ static_assert(sizeof(double) == sizeof(uint64_t), "double must be 64 bits");
+
+ const uint64_t* out_data;
+ if (!ReadWords(1, &out_data)) {
+ return false;
+ }
+ if (out != nullptr) {
+ memcpy(out, out_data, sizeof(double));
+ }
+ return true;
+}
+
+bool RecordCursor::ReadWords(size_t num_words, const uint64_t** data_out) {
+ const uint64_t* end =
+ reinterpret_cast<const uint64_t*>(tbv_.data() + tbv_.length());
+ const uint64_t* data =
+ reinterpret_cast<const uint64_t*>(tbv_.data()) + word_index_;
+ // This addition is unconditional so that callers with data_out == nullptr do
+ // not necessarily have to check the return value, as future calls will fail
+ // due to attempting to read out of bounds.
+ word_index_ += num_words;
+ if (data + num_words <= end) {
+ if (data_out != nullptr) {
+ *data_out = data;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
} // namespace fuchsia_trace_utils
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/fuchsia_trace_utils.h b/src/trace_processor/fuchsia_trace_utils.h
index c101dcf..3a70c86 100644
--- a/src/trace_processor/fuchsia_trace_utils.h
+++ b/src/trace_processor/fuchsia_trace_utils.h
@@ -22,6 +22,7 @@
#include <functional>
#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/trace_blob_view.h"
#include "src/trace_processor/trace_storage.h"
namespace perfetto {
@@ -40,12 +41,7 @@
}
bool IsInlineString(uint32_t);
-base::StringView ReadInlineString(const uint64_t**, uint32_t);
-
bool IsInlineThread(uint32_t);
-ThreadInfo ReadInlineThread(const uint64_t**);
-
-int64_t ReadTimestamp(const uint64_t**, uint64_t);
int64_t TicksToNs(uint64_t ticks, uint64_t ticks_per_second);
class ArgValue {
@@ -191,6 +187,35 @@
};
};
+// This class maintains a location into the record, with helper functions to
+// read various trace data from the current location in a safe manner.
+//
+// In the context of Fuchsia trace records, a "word" is defined as 64 bits
+// regardless of platform. For more information, see
+// https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/docs/development/tracing/trace-format/
+class RecordCursor {
+ public:
+ RecordCursor(const TraceBlobView* tbv) : tbv_(*tbv), word_index_(0) {}
+
+ size_t WordIndex();
+ void SetWordIndex(size_t index);
+
+ bool ReadTimestamp(uint64_t ticks_per_second, int64_t* ts_out);
+ bool ReadInlineString(uint32_t string_ref_or_len,
+ base::StringView* string_out);
+ bool ReadInlineThread(ThreadInfo* thread_out);
+
+ bool ReadInt64(int64_t* out);
+ bool ReadUint64(uint64_t* out);
+ bool ReadDouble(double* out);
+
+ private:
+ bool ReadWords(size_t num_words, const uint64_t** data_out);
+
+ const TraceBlobView& tbv_;
+ size_t word_index_;
+};
+
} // namespace fuchsia_trace_utils
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/graphics_event_parser.cc b/src/trace_processor/graphics_event_parser.cc
index 7265028..3276db8 100644
--- a/src/trace_processor/graphics_event_parser.cc
+++ b/src/trace_processor/graphics_event_parser.cc
@@ -456,6 +456,8 @@
vulkan_memory_event_row);
if (vulkan_memory_event.has_annotations()) {
+ auto global_row_id =
+ TraceStorage::CreateRowId(TableId::kVulkanMemoryAllocation, row_id);
for (auto itt = vulkan_memory_event.annotations(); itt; ++itt) {
protos::pbzero::VulkanMemoryEventAnnotation::Decoder annotation(
itt->data(), itt->size());
@@ -463,17 +465,17 @@
*(context_->vulkan_memory_tracker->FindString(annotation.key_iid()));
if (annotation.has_int_value()) {
context_->args_tracker->AddArg(
- row_id, annotation_id, annotation_id,
+ global_row_id, annotation_id, annotation_id,
Variadic::Integer(annotation.int_value()));
} else if (annotation.has_double_value()) {
context_->args_tracker->AddArg(
- row_id, annotation_id, annotation_id,
+ global_row_id, annotation_id, annotation_id,
Variadic::Real(annotation.double_value()));
} else if (annotation.has_string_iid()) {
context_->args_tracker->AddArg(
- row_id, annotation_id, annotation_id,
+ global_row_id, annotation_id, annotation_id,
Variadic::String(*(context_->vulkan_memory_tracker->FindString(
annotation.string_iid()))));
}
diff --git a/src/trace_processor/heap_graph_tracker.cc b/src/trace_processor/heap_graph_tracker.cc
index 57502e3..bd60ffa 100644
--- a/src/trace_processor/heap_graph_tracker.cc
+++ b/src/trace_processor/heap_graph_tracker.cc
@@ -22,20 +22,34 @@
HeapGraphTracker::HeapGraphTracker(TraceProcessorContext* context)
: context_(context) {}
-void HeapGraphTracker::AddObject(UniquePid upid, int64_t ts, SourceObject obj) {
+bool HeapGraphTracker::SetPidAndTimestamp(UniquePid upid, int64_t ts) {
if (current_upid_ != 0 && current_upid_ != upid) {
context_->storage->IncrementStats(stats::heap_graph_non_finalized_graph);
- return;
+ return false;
}
if (current_ts_ != 0 && current_ts_ != ts) {
context_->storage->IncrementStats(stats::heap_graph_non_finalized_graph);
- return;
+ return false;
}
current_upid_ = upid;
current_ts_ = ts;
+ return true;
+}
+
+void HeapGraphTracker::AddObject(UniquePid upid, int64_t ts, SourceObject obj) {
+ if (!SetPidAndTimestamp(upid, ts))
+ return;
+
current_objects_.emplace_back(std::move(obj));
}
+void HeapGraphTracker::AddRoot(UniquePid upid, int64_t ts, SourceRoot root) {
+ if (!SetPidAndTimestamp(upid, ts))
+ return;
+
+ current_roots_.emplace_back(std::move(root));
+}
+
void HeapGraphTracker::AddInternedTypeName(uint64_t intern_id,
StringPool::Id strid) {
interned_type_names_.emplace(intern_id, strid);
@@ -66,7 +80,7 @@
}
context_->storage->mutable_heap_graph_object_table()->Insert(
{current_upid_, current_ts_, static_cast<int64_t>(obj.object_id),
- static_cast<int64_t>(obj.self_size), -1, it->second});
+ static_cast<int64_t>(obj.self_size), -1, it->second, base::nullopt});
object_id_to_row_.emplace(
obj.object_id, context_->storage->heap_graph_object_table().size() - 1);
}
@@ -106,10 +120,26 @@
->mutable_reference_set_id()
->Set(static_cast<uint32_t>(owner_row), reference_set_id);
}
+
+ for (const SourceRoot& root : current_roots_) {
+ for (uint64_t obj_id : root.object_ids) {
+ auto it = object_id_to_row_.find(obj_id);
+ // This can only happen for an invalid type string id, which is already
+ // reported as an error. Silently continue here.
+ if (it == object_id_to_row_.end())
+ continue;
+
+ int64_t obj_row = it->second;
+ context_->storage->mutable_heap_graph_object_table()
+ ->mutable_root_type()
+ ->Set(static_cast<uint32_t>(obj_row), root.root_type);
+ }
+ }
interned_field_names_.clear();
object_id_to_row_.clear();
interned_type_names_.clear();
current_objects_.clear();
+ current_roots_.clear();
current_upid_ = 0;
current_ts_ = 0;
}
diff --git a/src/trace_processor/heap_graph_tracker.h b/src/trace_processor/heap_graph_tracker.h
index c65a7b1..7d0ec6a 100644
--- a/src/trace_processor/heap_graph_tracker.h
+++ b/src/trace_processor/heap_graph_tracker.h
@@ -46,8 +46,14 @@
std::vector<Reference> references;
};
+ struct SourceRoot {
+ StringPool::Id root_type;
+ std::vector<uint64_t> object_ids;
+ };
+
explicit HeapGraphTracker(TraceProcessorContext* context);
+ void AddRoot(UniquePid upid, int64_t ts, SourceRoot root);
void AddObject(UniquePid upid, int64_t ts, SourceObject obj);
void AddInternedTypeName(uint64_t intern_id, StringPool::Id strid);
void AddInternedFieldName(uint64_t intern_id, StringPool::Id strid);
@@ -55,11 +61,13 @@
void SetPacketIndex(uint64_t index);
private:
+ bool SetPidAndTimestamp(UniquePid upid, int64_t ts);
TraceProcessorContext* const context_;
UniquePid current_upid_ = 0;
int64_t current_ts_ = 0;
std::vector<SourceObject> current_objects_;
+ std::vector<SourceRoot> current_roots_;
std::map<uint64_t, StringPool::Id> interned_type_names_;
std::map<uint64_t, StringPool::Id> interned_field_names_;
std::map<uint64_t, int64_t> object_id_to_row_;
diff --git a/src/trace_processor/json_trace_parser.cc b/src/trace_processor/json_trace_parser.cc
index 54a548f..2216eab 100644
--- a/src/trace_processor/json_trace_parser.cc
+++ b/src/trace_processor/json_trace_parser.cc
@@ -45,12 +45,12 @@
void JsonTraceParser::ParseFtracePacket(uint32_t,
int64_t,
- TraceSorter::TimestampedTracePiece) {
+ TimestampedTracePiece) {
PERFETTO_FATAL("Json Trace Parser cannot handle ftrace packets.");
}
void JsonTraceParser::ParseTracePacket(int64_t timestamp,
- TraceSorter::TimestampedTracePiece ttp) {
+ TimestampedTracePiece ttp) {
PERFETTO_DCHECK(ttp.json_value != nullptr);
const Json::Value& value = *(ttp.json_value);
diff --git a/src/trace_processor/json_trace_parser.h b/src/trace_processor/json_trace_parser.h
index 9850924..bf0d5a5 100644
--- a/src/trace_processor/json_trace_parser.h
+++ b/src/trace_processor/json_trace_parser.h
@@ -23,8 +23,8 @@
#include <tuple>
#include <unordered_map>
+#include "src/trace_processor/timestamped_trace_piece.h"
#include "src/trace_processor/trace_parser.h"
-#include "src/trace_processor/trace_sorter.h"
#include "src/trace_processor/trace_storage.h"
namespace Json {
@@ -47,11 +47,8 @@
~JsonTraceParser() override;
// TraceParser implementation.
- void ParseTracePacket(int64_t timestamp,
- TraceSorter::TimestampedTracePiece) override;
- void ParseFtracePacket(uint32_t,
- int64_t,
- TraceSorter::TimestampedTracePiece) override;
+ void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) override;
+ void ParseFtracePacket(uint32_t, int64_t, TimestampedTracePiece) override;
private:
TraceProcessorContext* const context_;
diff --git a/src/trace_processor/json_trace_tokenizer.h b/src/trace_processor/json_trace_tokenizer.h
index 499342a..e5a1254 100644
--- a/src/trace_processor/json_trace_tokenizer.h
+++ b/src/trace_processor/json_trace_tokenizer.h
@@ -20,7 +20,6 @@
#include <stdint.h>
#include "src/trace_processor/chunked_trace_reader.h"
-#include "src/trace_processor/trace_sorter.h"
#include "src/trace_processor/trace_storage.h"
namespace Json {
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index 007f425..5a766d2 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -41,6 +41,7 @@
#include "src/trace_processor/stack_profile_tracker.h"
#include "src/trace_processor/syscall_tracker.h"
#include "src/trace_processor/systrace_parser.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
#include "src/trace_processor/trace_processor_context.h"
#include "src/trace_processor/track_tracker.h"
#include "src/trace_processor/variadic.h"
@@ -205,6 +206,43 @@
constexpr int64_t kPendingThreadInstructionDelta = -1;
} // namespace
+const char* HeapGraphRootTypeToString(int32_t type) {
+ switch (type) {
+ case protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN:
+ return "ROOT_UNKNOWN";
+ case protos::pbzero::HeapGraphRoot::ROOT_JNI_GLOBAL:
+ return "ROOT_JNI_GLOBAL";
+ case protos::pbzero::HeapGraphRoot::ROOT_JNI_LOCAL:
+ return "ROOT_JNI_LOCAL";
+ case protos::pbzero::HeapGraphRoot::ROOT_JAVA_FRAME:
+ return "ROOT_JAVA_FRAME";
+ case protos::pbzero::HeapGraphRoot::ROOT_NATIVE_STACK:
+ return "ROOT_NATIVE_STACK";
+ case protos::pbzero::HeapGraphRoot::ROOT_STICKY_CLASS:
+ return "ROOT_STICKY_CLASS";
+ case protos::pbzero::HeapGraphRoot::ROOT_THREAD_BLOCK:
+ return "ROOT_THREAD_BLOCK";
+ case protos::pbzero::HeapGraphRoot::ROOT_MONITOR_USED:
+ return "ROOT_MONITOR_USED";
+ case protos::pbzero::HeapGraphRoot::ROOT_THREAD_OBJECT:
+ return "ROOT_THREAD_OBJECT";
+ case protos::pbzero::HeapGraphRoot::ROOT_INTERNED_STRING:
+ return "ROOT_INTERNED_STRING";
+ case protos::pbzero::HeapGraphRoot::ROOT_FINALIZING:
+ return "ROOT_FINALIZING";
+ case protos::pbzero::HeapGraphRoot::ROOT_DEBUGGER:
+ return "ROOT_DEBUGGER";
+ case protos::pbzero::HeapGraphRoot::ROOT_REFERENCE_CLEANUP:
+ return "ROOT_REFERENCE_CLEANUP";
+ case protos::pbzero::HeapGraphRoot::ROOT_VM_INTERNAL:
+ return "ROOT_VM_INTERNAL";
+ case protos::pbzero::HeapGraphRoot::ROOT_JNI_MONITOR:
+ return "ROOT_JNI_MONITOR";
+ default:
+ return "ROOT_UNKNOWN";
+ }
+}
+
} // namespace
ProtoTraceParser::ProtoTraceParser(TraceProcessorContext* context)
@@ -394,9 +432,7 @@
ProtoTraceParser::~ProtoTraceParser() = default;
-void ProtoTraceParser::ParseTracePacket(
- int64_t ts,
- TraceSorter::TimestampedTracePiece ttp) {
+void ProtoTraceParser::ParseTracePacket(int64_t ts, TimestampedTracePiece ttp) {
PERFETTO_DCHECK(ttp.json_value == nullptr);
const TraceBlobView& blob = ttp.blob_view;
@@ -677,14 +713,13 @@
}
}
-void ProtoTraceParser::ParseFtracePacket(
- uint32_t cpu,
- int64_t ts,
- TraceSorter::TimestampedTracePiece ttp) {
+void ProtoTraceParser::ParseFtracePacket(uint32_t cpu,
+ int64_t ts,
+ TimestampedTracePiece ttp) {
PERFETTO_DCHECK(ttp.json_value == nullptr);
// Handle the (optional) alternative encoding format for sched_switch.
- if (ttp.inline_event.type == TraceSorter::InlineEvent::Type::kSchedSwitch) {
+ if (ttp.inline_event.type == InlineEvent::Type::kSchedSwitch) {
const auto& event = ttp.inline_event.sched_switch;
context_->event_tracker->PushSchedSwitchCompact(
cpu, ts, event.prev_state, static_cast<uint32_t>(event.next_pid),
@@ -2686,6 +2721,17 @@
context_->heap_graph_tracker->AddInternedFieldName(
entry.iid(), context_->storage->InternString(str_view));
}
+ for (auto it = heap_graph.roots(); it; ++it) {
+ protos::pbzero::HeapGraphRoot::Decoder entry(it->data(), it->size());
+ const char* str = HeapGraphRootTypeToString(entry.root_type());
+ auto str_view = base::StringView(str);
+
+ HeapGraphTracker::SourceRoot src_root;
+ src_root.root_type = context_->storage->InternString(str_view);
+ for (auto obj_it = entry.object_ids(); obj_it; ++obj_it)
+ src_root.object_ids.emplace_back(obj_it->as_uint64());
+ context_->heap_graph_tracker->AddRoot(upid, ts, std::move(src_root));
+ }
if (!heap_graph.continued()) {
context_->heap_graph_tracker->FinalizeProfile();
}
diff --git a/src/trace_processor/proto_trace_parser.h b/src/trace_processor/proto_trace_parser.h
index 15f841e..7416702 100644
--- a/src/trace_processor/proto_trace_parser.h
+++ b/src/trace_processor/proto_trace_parser.h
@@ -29,10 +29,12 @@
#include "src/trace_processor/graphics_event_parser.h"
#include "src/trace_processor/proto_incremental_state.h"
#include "src/trace_processor/slice_tracker.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
#include "src/trace_processor/trace_blob_view.h"
#include "src/trace_processor/trace_parser.h"
#include "src/trace_processor/trace_storage.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
namespace perfetto {
@@ -48,11 +50,10 @@
~ProtoTraceParser() override;
// TraceParser implementation.
- void ParseTracePacket(int64_t timestamp,
- TraceSorter::TimestampedTracePiece) override;
+ void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) override;
void ParseFtracePacket(uint32_t cpu,
int64_t timestamp,
- TraceSorter::TimestampedTracePiece) override;
+ TimestampedTracePiece) override;
void ParseProcessTree(ConstBytes);
void ParseProcessStats(int64_t timestamp, ConstBytes);
diff --git a/src/trace_processor/proto_trace_tokenizer.cc b/src/trace_processor/proto_trace_tokenizer.cc
index e27ae54..70b6c0b 100644
--- a/src/trace_processor/proto_trace_tokenizer.cc
+++ b/src/trace_processor/proto_trace_tokenizer.cc
@@ -803,7 +803,7 @@
auto comm_it = compact.switch_next_comm_index(&parse_error);
for (; timestamp_it && pstate_it && npid_it && nprio_it && comm_it;
++timestamp_it, ++pstate_it, ++npid_it, ++nprio_it, ++comm_it) {
- TraceSorter::InlineSchedSwitch event{};
+ InlineSchedSwitch event{};
// delta-encoded timestamp
timestamp_acc += static_cast<int64_t>(*timestamp_it);
@@ -817,8 +817,8 @@
event.next_pid = *npid_it;
event.next_prio = *nprio_it;
- context_->sorter->PushInlineFtraceEvent(
- cpu, event_timestamp, TraceSorter::InlineEvent::SchedSwitch(event));
+ context_->sorter->PushInlineFtraceEvent(cpu, event_timestamp,
+ InlineEvent::SchedSwitch(event));
}
// Check that all packed buffers were decoded correctly, and fully.
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index 16133d8..c81ec7c 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -41,7 +41,8 @@
C(int64_t, object_id) \
C(int64_t, self_size) \
C(int64_t, reference_set_id) \
- C(StringPool::Id, type_name)
+ C(StringPool::Id, type_name) \
+ C(base::Optional<StringPool::Id>, root_type)
PERFETTO_TP_TABLE(PERFETTO_TP_HEAP_GRAPH_OBJECT_DEF);
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
new file mode 100644
index 0000000..1a4f346
--- /dev/null
+++ b/src/trace_processor/timestamped_trace_piece.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_
+#define SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/fuchsia_provider_view.h"
+#include "src/trace_processor/proto_incremental_state.h"
+#include "src/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
+#include <json/value.h>
+#else
+// Json traces are only supported in standalone and Chromium builds.
+namespace Json {
+class Value {};
+} // namespace Json
+#endif
+
+namespace perfetto {
+namespace trace_processor {
+
+struct InlineSchedSwitch {
+ int64_t prev_state;
+ int32_t next_pid;
+ int32_t next_prio;
+ StringId next_comm;
+};
+
+// Discriminated union of events that are cannot be easily read from the
+// mapped trace.
+struct InlineEvent {
+ enum class Type { kInvalid = 0, kSchedSwitch };
+
+ static InlineEvent SchedSwitch(InlineSchedSwitch content) {
+ InlineEvent evt;
+ evt.type = Type::kSchedSwitch;
+ evt.sched_switch = content;
+ return evt;
+ }
+
+ Type type = Type::kInvalid;
+ union {
+ InlineSchedSwitch sched_switch;
+ };
+};
+
+// A TimestampedTracePiece is (usually a reference to) a piece of a trace that
+// is sorted by TraceSorter.
+struct TimestampedTracePiece {
+ TimestampedTracePiece(
+ int64_t ts,
+ uint64_t idx,
+ TraceBlobView tbv,
+ ProtoIncrementalState::PacketSequenceState* sequence_state)
+ : TimestampedTracePiece(ts,
+ /*thread_ts=*/0,
+ /*thread_instructions=*/0,
+ idx,
+ std::move(tbv),
+ /*value=*/nullptr,
+ /*fpv=*/nullptr,
+ /*sequence_state=*/sequence_state,
+ InlineEvent{}) {}
+
+ TimestampedTracePiece(int64_t ts, uint64_t idx, TraceBlobView tbv)
+ : TimestampedTracePiece(ts,
+ /*thread_ts=*/0,
+ /*thread_instructions=*/0,
+ idx,
+ std::move(tbv),
+ /*value=*/nullptr,
+ /*fpv=*/nullptr,
+ /*sequence_state=*/nullptr,
+ InlineEvent{}) {}
+
+ TimestampedTracePiece(int64_t ts,
+ uint64_t idx,
+ std::unique_ptr<Json::Value> value)
+ : TimestampedTracePiece(ts,
+ /*thread_ts=*/0,
+ /*thread_instructions=*/0,
+ idx,
+ // TODO(dproy): Stop requiring TraceBlobView in
+ // TimestampedTracePiece.
+ TraceBlobView(nullptr, 0, 0),
+ std::move(value),
+ /*fpv=*/nullptr,
+ /*sequence_state=*/nullptr,
+ InlineEvent{}) {}
+
+ TimestampedTracePiece(int64_t ts,
+ uint64_t idx,
+ TraceBlobView tbv,
+ std::unique_ptr<FuchsiaProviderView> fpv)
+ : TimestampedTracePiece(ts,
+ /*thread_ts=*/0,
+ /*thread_instructions=*/0,
+ idx,
+ std::move(tbv),
+ /*value=*/nullptr,
+ std::move(fpv),
+ /*sequence_state=*/nullptr,
+ InlineEvent{}) {}
+
+ TimestampedTracePiece(
+ int64_t ts,
+ int64_t thread_ts,
+ int64_t thread_instructions,
+ uint64_t idx,
+ TraceBlobView tbv,
+ ProtoIncrementalState::PacketSequenceState* sequence_state)
+ : TimestampedTracePiece(ts,
+ thread_ts,
+ thread_instructions,
+ idx,
+ std::move(tbv),
+ /*value=*/nullptr,
+ /*fpv=*/nullptr,
+ sequence_state,
+ InlineEvent{}) {}
+
+ TimestampedTracePiece(int64_t ts, uint64_t idx, InlineEvent inline_evt)
+ : TimestampedTracePiece(ts,
+ /*thread_ts=*/0,
+ /*thread_instructions=*/0,
+ idx,
+ /*tbv=*/TraceBlobView(nullptr, 0, 0),
+ /*value=*/nullptr,
+ /*fpv=*/nullptr,
+ /*sequence_state=*/nullptr,
+ inline_evt) {}
+
+ TimestampedTracePiece(
+ int64_t ts,
+ int64_t thread_ts,
+ int64_t thread_instructions,
+ uint64_t idx,
+ TraceBlobView tbv,
+ std::unique_ptr<Json::Value> value,
+ std::unique_ptr<FuchsiaProviderView> fpv,
+ ProtoIncrementalState::PacketSequenceState* sequence_state,
+ InlineEvent inline_evt)
+ : json_value(std::move(value)),
+ fuchsia_provider_view(std::move(fpv)),
+ packet_sequence_state(sequence_state),
+ packet_sequence_state_generation(
+ sequence_state ? sequence_state->current_generation() : 0),
+ timestamp(ts),
+ thread_timestamp(thread_ts),
+ thread_instruction_count(thread_instructions),
+ packet_idx_(idx),
+ blob_view(std::move(tbv)),
+ inline_event(inline_evt) {}
+
+ TimestampedTracePiece(TimestampedTracePiece&&) noexcept = default;
+ TimestampedTracePiece& operator=(TimestampedTracePiece&&) = default;
+
+ // For std::lower_bound().
+ static inline bool Compare(const TimestampedTracePiece& x, int64_t ts) {
+ return x.timestamp < ts;
+ }
+
+ // For std::sort().
+ inline bool operator<(const TimestampedTracePiece& o) const {
+ return timestamp < o.timestamp ||
+ (timestamp == o.timestamp && packet_idx_ < o.packet_idx_);
+ }
+
+ std::unique_ptr<Json::Value> json_value;
+ std::unique_ptr<FuchsiaProviderView> fuchsia_provider_view;
+ ProtoIncrementalState::PacketSequenceState* packet_sequence_state;
+ size_t packet_sequence_state_generation;
+
+ int64_t timestamp;
+ int64_t thread_timestamp;
+ int64_t thread_instruction_count;
+ uint64_t packet_idx_;
+ TraceBlobView blob_view;
+ InlineEvent inline_event;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index d070664..58242f6 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -151,7 +151,7 @@
ASSERT_GT(it.Get(0).long_value, 0);
}
-TEST_F(TraceProcessorIntegrationTest, DISABLED_Clusterfuzz14767) {
+TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14767) {
ASSERT_TRUE(LoadTrace("clusterfuzz_14767", 4096 * 1024).ok());
auto it = Query("select sum(value) from stats where severity = 'error';");
ASSERT_TRUE(it.Next());
diff --git a/src/trace_processor/trace_parser.h b/src/trace_processor/trace_parser.h
index 63c2ce1..fa3b4ef 100644
--- a/src/trace_processor/trace_parser.h
+++ b/src/trace_processor/trace_parser.h
@@ -19,7 +19,7 @@
#include <stdint.h>
-#include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
namespace perfetto {
namespace trace_processor {
@@ -28,11 +28,10 @@
public:
virtual ~TraceParser();
- virtual void ParseTracePacket(int64_t timestamp,
- TraceSorter::TimestampedTracePiece) = 0;
+ virtual void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) = 0;
virtual void ParseFtracePacket(uint32_t cpu,
int64_t timestamp,
- TraceSorter::TimestampedTracePiece) = 0;
+ TimestampedTracePiece) = 0;
};
} // namespace trace_processor
diff --git a/src/trace_processor/trace_sorter.h b/src/trace_processor/trace_sorter.h
index d0df2cf..4aababf 100644
--- a/src/trace_processor/trace_sorter.h
+++ b/src/trace_processor/trace_sorter.h
@@ -23,18 +23,14 @@
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/fuchsia_provider_view.h"
#include "src/trace_processor/proto_incremental_state.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
#include "src/trace_processor/trace_blob_view.h"
#include "src/trace_processor/trace_processor_context.h"
#include "src/trace_processor/trace_storage.h"
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
-#include <json/value.h>
-#else
-// Json traces are only supported in standalone and Chromium builds.
namespace Json {
-class Value {};
+class Value;
} // namespace Json
-#endif
namespace perfetto {
namespace trace_processor {
@@ -68,164 +64,6 @@
// from there to the end.
class TraceSorter {
public:
- struct InlineSchedSwitch {
- int64_t prev_state;
- int32_t next_pid;
- int32_t next_prio;
- StringId next_comm;
- };
-
- // Discriminated union of events that are cannot be easily read from the
- // mapped trace.
- struct InlineEvent {
- enum class Type { kInvalid = 0, kSchedSwitch };
-
- static InlineEvent SchedSwitch(InlineSchedSwitch content) {
- InlineEvent evt;
- evt.type = Type::kSchedSwitch;
- evt.sched_switch = content;
- return evt;
- }
-
- Type type = Type::kInvalid;
- union {
- InlineSchedSwitch sched_switch;
- };
- };
-
- struct TimestampedTracePiece {
- TimestampedTracePiece(
- int64_t ts,
- uint64_t idx,
- TraceBlobView tbv,
- ProtoIncrementalState::PacketSequenceState* sequence_state)
- : TimestampedTracePiece(ts,
- /*thread_ts=*/0,
- /*thread_instructions=*/0,
- idx,
- std::move(tbv),
- /*value=*/nullptr,
- /*fpv=*/nullptr,
- /*sequence_state=*/sequence_state,
- InlineEvent{}) {}
-
- TimestampedTracePiece(int64_t ts, uint64_t idx, TraceBlobView tbv)
- : TimestampedTracePiece(ts,
- /*thread_ts=*/0,
- /*thread_instructions=*/0,
- idx,
- std::move(tbv),
- /*value=*/nullptr,
- /*fpv=*/nullptr,
- /*sequence_state=*/nullptr,
- InlineEvent{}) {}
-
- TimestampedTracePiece(int64_t ts,
- uint64_t idx,
- std::unique_ptr<Json::Value> value)
- : TimestampedTracePiece(ts,
- /*thread_ts=*/0,
- /*thread_instructions=*/0,
- idx,
- // TODO(dproy): Stop requiring TraceBlobView in
- // TimestampedTracePiece.
- TraceBlobView(nullptr, 0, 0),
- std::move(value),
- /*fpv=*/nullptr,
- /*sequence_state=*/nullptr,
- InlineEvent{}) {}
-
- TimestampedTracePiece(int64_t ts,
- uint64_t idx,
- TraceBlobView tbv,
- std::unique_ptr<FuchsiaProviderView> fpv)
- : TimestampedTracePiece(ts,
- /*thread_ts=*/0,
- /*thread_instructions=*/0,
- idx,
- std::move(tbv),
- /*value=*/nullptr,
- std::move(fpv),
- /*sequence_state=*/nullptr,
- InlineEvent{}) {}
-
- TimestampedTracePiece(
- int64_t ts,
- int64_t thread_ts,
- int64_t thread_instructions,
- uint64_t idx,
- TraceBlobView tbv,
- ProtoIncrementalState::PacketSequenceState* sequence_state)
- : TimestampedTracePiece(ts,
- thread_ts,
- thread_instructions,
- idx,
- std::move(tbv),
- /*value=*/nullptr,
- /*fpv=*/nullptr,
- sequence_state,
- InlineEvent{}) {}
-
- TimestampedTracePiece(int64_t ts, uint64_t idx, InlineEvent inline_evt)
- : TimestampedTracePiece(ts,
- /*thread_ts=*/0,
- /*thread_instructions=*/0,
- idx,
- /*tbv=*/TraceBlobView(nullptr, 0, 0),
- /*value=*/nullptr,
- /*fpv=*/nullptr,
- /*sequence_state=*/nullptr,
- inline_evt) {}
-
- TimestampedTracePiece(
- int64_t ts,
- int64_t thread_ts,
- int64_t thread_instructions,
- uint64_t idx,
- TraceBlobView tbv,
- std::unique_ptr<Json::Value> value,
- std::unique_ptr<FuchsiaProviderView> fpv,
- ProtoIncrementalState::PacketSequenceState* sequence_state,
- InlineEvent inline_evt)
- : json_value(std::move(value)),
- fuchsia_provider_view(std::move(fpv)),
- packet_sequence_state(sequence_state),
- packet_sequence_state_generation(
- sequence_state ? sequence_state->current_generation() : 0),
- timestamp(ts),
- thread_timestamp(thread_ts),
- thread_instruction_count(thread_instructions),
- packet_idx_(idx),
- blob_view(std::move(tbv)),
- inline_event(inline_evt) {}
-
- TimestampedTracePiece(TimestampedTracePiece&&) noexcept = default;
- TimestampedTracePiece& operator=(TimestampedTracePiece&&) = default;
-
- // For std::lower_bound().
- static inline bool Compare(const TimestampedTracePiece& x, int64_t ts) {
- return x.timestamp < ts;
- }
-
- // For std::sort().
- inline bool operator<(const TimestampedTracePiece& o) const {
- return timestamp < o.timestamp ||
- (timestamp == o.timestamp && packet_idx_ < o.packet_idx_);
- }
-
- std::unique_ptr<Json::Value> json_value;
- std::unique_ptr<FuchsiaProviderView> fuchsia_provider_view;
- ProtoIncrementalState::PacketSequenceState* packet_sequence_state;
- size_t packet_sequence_state_generation;
-
- int64_t timestamp;
- int64_t thread_timestamp;
- int64_t thread_instruction_count;
- uint64_t packet_idx_;
- TraceBlobView blob_view;
- InlineEvent inline_event;
- };
-
TraceSorter(TraceProcessorContext*, int64_t window_size_ns);
inline void PushTracePacket(int64_t timestamp,
diff --git a/src/trace_processor/trace_sorter_unittest.cc b/src/trace_processor/trace_sorter_unittest.cc
index 116902e..da93c61 100644
--- a/src/trace_processor/trace_sorter_unittest.cc
+++ b/src/trace_processor/trace_sorter_unittest.cc
@@ -20,6 +20,7 @@
#include <vector>
#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
#include "src/trace_processor/trace_processor_context.h"
#include "src/trace_processor/trace_sorter.h"
#include "test/gtest_and_gmock.h"
@@ -46,7 +47,7 @@
void ParseFtracePacket(uint32_t cpu,
int64_t timestamp,
- TraceSorter::TimestampedTracePiece ttp) override {
+ TimestampedTracePiece ttp) override {
TraceBlobView& tbv = ttp.blob_view;
MOCK_ParseFtracePacket(cpu, timestamp, tbv.data(), tbv.length());
}
@@ -54,8 +55,7 @@
MOCK_METHOD3(MOCK_ParseTracePacket,
void(int64_t ts, const uint8_t* data, size_t length));
- void ParseTracePacket(int64_t ts,
- TraceSorter::TimestampedTracePiece ttp) override {
+ void ParseTracePacket(int64_t ts, TimestampedTracePiece ttp) override {
TraceBlobView& tbv = ttp.blob_view;
MOCK_ParseTracePacket(ts, tbv.data(), tbv.length());
}
diff --git a/src/trace_processor/trace_storage.cc b/src/trace_processor/trace_storage.cc
index 662c460..1b9f256 100644
--- a/src/trace_processor/trace_storage.cc
+++ b/src/trace_processor/trace_storage.cc
@@ -131,6 +131,9 @@
if (start_ns == std::numeric_limits<int64_t>::max()) {
return std::make_pair(0, 0);
}
+ if (start_ns == end_ns) {
+ end_ns += 1;
+ }
return std::make_pair(start_ns, end_ns);
}
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index 3ad1316..a2f26b7 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -73,14 +73,19 @@
ASSERT_GT(packets.size(), 0);
size_t objects = 0;
- for (const auto& packet : packets)
+ size_t roots = 0;
+ for (const auto& packet : packets) {
objects += packet.heap_graph().objects_size();
+ roots += packet.heap_graph().roots_size();
+ }
ASSERT_GT(objects, 0);
+ ASSERT_GT(roots, 0);
}
void AssertNoProfileContents(std::vector<protos::TracePacket> packets) {
// If profile packets are present, they must be empty.
for (const auto& packet : packets) {
+ ASSERT_EQ(packet.heap_graph().roots_size(), 0);
ASSERT_EQ(packet.heap_graph().objects_size(), 0);
ASSERT_EQ(packet.heap_graph().type_names_size(), 0);
ASSERT_EQ(packet.heap_graph().field_names_size(), 0);
diff --git a/test/metrics/heap_profile.textproto b/test/metrics/heap_profile.textproto
index e64cb5c..831b002 100644
--- a/test/metrics/heap_profile.textproto
+++ b/test/metrics/heap_profile.textproto
@@ -14,6 +14,8 @@
}
packet {
trusted_packet_sequence_id: 999
+ previous_packet_dropped: true
+ incremental_state_cleared: true
timestamp: 10
profile_packet {
strings {
diff --git a/test/trace_processor/heap_graph.textproto b/test/trace_processor/heap_graph.textproto
index 64cca48..eb32c54 100644
--- a/test/trace_processor/heap_graph.textproto
+++ b/test/trace_processor/heap_graph.textproto
@@ -16,6 +16,10 @@
trusted_packet_sequence_id: 999
timestamp: 10
heap_graph {
+ roots {
+ root_type: ROOT_JAVA_FRAME
+ object_ids: 0x01
+ }
objects {
id: 0x01
type_id: 1
@@ -31,6 +35,8 @@
continued: true
index: 1
}
+}
+packet {
heap_graph {
type_names {
iid: 1
diff --git a/test/trace_processor/heap_graph_object.out b/test/trace_processor/heap_graph_object.out
index fc3b7a4..3f43952 100644
--- a/test/trace_processor/heap_graph_object.out
+++ b/test/trace_processor/heap_graph_object.out
@@ -1,3 +1,3 @@
-"id","type","upid","graph_sample_ts","object_id","self_size","reference_set_id","type_name"
-0,"heap_graph_object",0,10,1,64,0,"FactoryProducerDelegateImplActor"
-1,"heap_graph_object",0,10,2,32,1,"Foo"
+"id","type","upid","graph_sample_ts","object_id","self_size","reference_set_id","type_name","root_type"
+0,"heap_graph_object",0,10,1,64,0,"FactoryProducerDelegateImplActor","ROOT_JAVA_FRAME"
+1,"heap_graph_object",0,10,2,32,1,"Foo","[NULL]"
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index de152ea..cb99f09 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -12,11 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {LoadingManager} from '../controller/loading_manager';
-
import {RawQueryResult, TraceProcessor} from './protos';
import {TimeSpan} from './time';
+export interface LoadingTracker {
+ beginLoading(): void;
+ endLoading(): void;
+}
+
+export class NullLoadingTracker implements LoadingTracker {
+ beginLoading(): void {}
+
+ endLoading(): void {}
+}
+
/**
* Abstract interface of a trace proccessor.
* This class is wrapper for multiple proto services defined in:
@@ -32,10 +41,10 @@
abstract readonly id: string;
private _cpus?: number[];
private _numGpus?: number;
- private loadingManager: LoadingManager;
+ private loadingTracker: LoadingTracker;
- constructor() {
- this.loadingManager = LoadingManager.getInstance;
+ constructor(tracker?: LoadingTracker) {
+ this.loadingTracker = tracker ? tracker : new NullLoadingTracker();
}
/**
@@ -60,9 +69,9 @@
*/
query(sqlQuery: string): Promise<RawQueryResult> {
const timeQueuedNs = Math.floor(performance.now() * 1e6);
- this.loadingManager.beginLoading();
+ this.loadingTracker.beginLoading();
return this.rpc.rawQuery({sqlQuery, timeQueuedNs}).finally(() => {
- this.loadingManager.endLoading();
+ this.loadingTracker.endLoading();
});
}
diff --git a/ui/src/common/wasm_engine_proxy.ts b/ui/src/common/wasm_engine_proxy.ts
index 449a83b..8194894 100644
--- a/ui/src/common/wasm_engine_proxy.ts
+++ b/ui/src/common/wasm_engine_proxy.ts
@@ -13,13 +13,13 @@
// limitations under the License.
import * as protobufjs from 'protobufjs/light';
+import {Message, Method, rpc} from 'protobufjs/light';
import {defer} from '../base/deferred';
import {WasmBridgeRequest, WasmBridgeResponse} from '../engine/wasm_bridge';
-import {Engine} from './engine';
+import {Engine, LoadingTracker} from './engine';
import {TraceProcessor} from './protos';
-import {Method, rpc, Message} from 'protobufjs/light';
const activeWorkers = new Map<string, Worker>();
let warmWorker: null|Worker = null;
@@ -77,8 +77,9 @@
private nextRequestId: number;
readonly id: string;
- constructor(args: {id: string, worker: Worker}) {
- super();
+ constructor(
+ args: {id: string, loadingTracker?: LoadingTracker, worker: Worker}) {
+ super(args.loadingTracker);
this.nextRequestId = 0;
this.pendingCallbacks = new Map();
this.id = args.id;
diff --git a/ui/src/controller/globals.ts b/ui/src/controller/globals.ts
index 179c0ee..244c6cc 100644
--- a/ui/src/controller/globals.ts
+++ b/ui/src/controller/globals.ts
@@ -26,6 +26,7 @@
} from '../common/wasm_engine_proxy';
import {ControllerAny} from './controller';
+import {LoadingManager} from './loading_manager';
type PublishKinds =
'OverviewData'|'TrackData'|'Threads'|'QueryResult'|'LegacyTrace'|
@@ -94,8 +95,11 @@
createEngine(): Engine {
const id = new Date().toUTCString();
- const portAndId = {id, worker: createWasmEngine(id)};
- return new WasmEngineProxy(portAndId);
+ return new WasmEngineProxy({
+ id,
+ worker: createWasmEngine(id),
+ loadingTracker: LoadingManager.getInstance,
+ });
}
destroyEngine(id: string): void {
diff --git a/ui/src/controller/loading_manager.ts b/ui/src/controller/loading_manager.ts
index afaec74..64c7123 100644
--- a/ui/src/controller/loading_manager.ts
+++ b/ui/src/controller/loading_manager.ts
@@ -12,14 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {LoadingTracker} from '../common/engine';
+
import {globals} from './globals';
// Used to keep track of whether the engine is currently querying.
-export class LoadingManager {
+export class LoadingManager implements LoadingTracker {
private static _instance: LoadingManager;
private currentlyLoading = 0;
- static get getInstance() {
+ static get getInstance(): LoadingManager {
return this._instance || (this._instance = new this());
}
@@ -43,4 +45,4 @@
private isLoading() {
return this.currentlyLoading > 0;
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index 4e8afe9..2257307 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -116,7 +116,7 @@
}
async sliceDetails(id: number) {
- const sqlQuery = `SELECT ts, dur, priority, end_state, utid FROM sched
+ const sqlQuery = `SELECT ts, dur, priority, end_state, utid, cpu FROM sched
WHERE row_id = ${id}`;
this.args.engine.query(sqlQuery).then(result => {
// Check selection is still the same on completion of query.
@@ -128,8 +128,9 @@
const priority = result.columns[2].longValues![0] as number;
const endState = result.columns[3].stringValues![0];
const utid = result.columns[4].longValues![0] as number;
+ const cpu = result.columns[5].longValues![0] as number;
const selected: SliceDetails =
- {ts: timeFromStart, dur, priority, endState, id, utid};
+ {ts: timeFromStart, dur, priority, endState, cpu, id, utid};
this.schedulingDetails(ts, utid).then(wakeResult => {
Object.assign(selected, wakeResult);
globals.publish('SliceDetails', selected);
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index d6c83cb..5cf35ab 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -495,7 +495,8 @@
utid === null ? undefined : utidToThreadTrack.get(utid);
if (threadTrack === undefined &&
(upid === null || counterUpids[upid] === undefined) &&
- counterUtids[utid] === undefined && !threadHasSched) {
+ counterUtids[utid] === undefined && !threadHasSched &&
+ (upid === null || upid !== null && !heapUpids.has(upid))) {
continue;
}
@@ -523,11 +524,16 @@
config: {pidForColor, upid, utid},
});
+ const name = upid === null ?
+ `${threadName} ${tid}` :
+ `${
+ processName === null && heapUpids.has(upid) ?
+ 'Heap Profile for' :
+ processName} ${pid}`;
addTrackGroupActions.push(Actions.addTrackGroup({
engineId: this.engineId,
summaryTrackId,
- name: upid === null ? `${threadName} ${tid}` :
- `${processName} ${pid}`,
+ name,
id: pUuid,
collapsed: true,
}));
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
new file mode 100644
index 0000000..1bed6ea
--- /dev/null
+++ b/ui/src/frontend/details_panel.ts
@@ -0,0 +1,181 @@
+// Copyright (C) 2019 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 * as m from 'mithril';
+
+import {LogExists, LogExistsKey} from '../common/logs';
+
+import {ChromeSliceDetailsPanel} from './chrome_slice_panel';
+import {CounterDetailsPanel} from './counter_panel';
+import {DragGestureHandler} from './drag_gesture_handler';
+import {globals} from './globals';
+import {HeapProfileDetailsPanel} from './heap_profile_panel';
+import {LogPanel} from './logs_panel';
+import {NotesEditorPanel} from './notes_panel';
+import {AnyAttrsVnode, PanelContainer} from './panel_container';
+import {SliceDetailsPanel} from './slice_panel';
+import {ThreadStatePanel} from './thread_state_panel';
+
+const UP_ICON = 'keyboard_arrow_up';
+const DOWN_ICON = 'keyboard_arrow_down';
+const DRAG_HANDLE_HEIGHT_PX = 28;
+const DEFAULT_DETAILS_HEIGHT_PX = 230 + DRAG_HANDLE_HEIGHT_PX;
+
+function hasLogs(): boolean {
+ const data = globals.trackDataStore.get(LogExistsKey) as LogExists;
+ return data && data.exists;
+}
+
+interface DragHandleAttrs {
+ height: number;
+ resize: (height: number) => void;
+}
+
+class DragHandle implements m.ClassComponent<DragHandleAttrs> {
+ private dragStartHeight = 0;
+ private height = 0;
+ private resize: (height: number) => void = () => {};
+ private isClosed = this.height <= DRAG_HANDLE_HEIGHT_PX;
+
+ oncreate({dom, attrs}: m.CVnodeDOM<DragHandleAttrs>) {
+ this.resize = attrs.resize;
+ this.height = attrs.height;
+ this.isClosed = this.height <= DRAG_HANDLE_HEIGHT_PX;
+ const elem = dom as HTMLElement;
+ new DragGestureHandler(
+ elem,
+ this.onDrag.bind(this),
+ this.onDragStart.bind(this),
+ this.onDragEnd.bind(this));
+ }
+
+ onupdate({attrs}: m.CVnodeDOM<DragHandleAttrs>) {
+ this.resize = attrs.resize;
+ this.height = attrs.height;
+ this.isClosed = this.height <= DRAG_HANDLE_HEIGHT_PX;
+ }
+
+ onDrag(_x: number, y: number) {
+ const newHeight = this.dragStartHeight + (DRAG_HANDLE_HEIGHT_PX / 2) - y;
+ this.isClosed = Math.floor(newHeight) <= DRAG_HANDLE_HEIGHT_PX;
+ this.resize(Math.floor(newHeight));
+ globals.rafScheduler.scheduleFullRedraw();
+ }
+
+ onDragStart(_x: number, _y: number) {
+ this.dragStartHeight = this.height;
+ }
+
+ onDragEnd() {}
+
+ view() {
+ const icon = this.isClosed ? UP_ICON : DOWN_ICON;
+ const title = this.isClosed ? 'Show panel' : 'Hide panel';
+ return m(
+ '.handle',
+ m('.handle-title', 'Current Selection'),
+ m('i.material-icons',
+ {
+ onclick: () => {
+ if (this.height === DRAG_HANDLE_HEIGHT_PX) {
+ this.isClosed = false;
+ this.resize(DEFAULT_DETAILS_HEIGHT_PX);
+ } else {
+ this.isClosed = true;
+ this.resize(DRAG_HANDLE_HEIGHT_PX);
+ }
+ globals.rafScheduler.scheduleFullRedraw();
+ },
+ title
+ },
+ icon));
+ }
+}
+
+export class DetailsPanel implements m.ClassComponent {
+ private detailsHeight = DRAG_HANDLE_HEIGHT_PX;
+ // Used to set details panel to default height on selection.
+ private showDetailsPanel = true;
+
+ view() {
+ const detailsPanels: AnyAttrsVnode[] = [];
+ const curSelection = globals.state.currentSelection;
+ if (curSelection) {
+ switch (curSelection.kind) {
+ case 'NOTE':
+ detailsPanels.push(m(NotesEditorPanel, {
+ key: 'notes',
+ id: curSelection.id,
+ }));
+ break;
+ case 'SLICE':
+ detailsPanels.push(m(SliceDetailsPanel, {
+ key: 'slice',
+ }));
+ break;
+ case 'COUNTER':
+ detailsPanels.push(m(CounterDetailsPanel, {
+ key: 'counter',
+ }));
+ break;
+ case 'HEAP_PROFILE':
+ detailsPanels.push(m(HeapProfileDetailsPanel, {key: 'heap_profile'}));
+ break;
+ case 'CHROME_SLICE':
+ detailsPanels.push(m(ChromeSliceDetailsPanel));
+ break;
+ case 'THREAD_STATE':
+ detailsPanels.push(m(ThreadStatePanel, {
+ key: 'thread_state',
+ ts: curSelection.ts,
+ dur: curSelection.dur,
+ utid: curSelection.utid,
+ state: curSelection.state,
+ cpu: curSelection.cpu
+ }));
+ break;
+ default:
+ break;
+ }
+ } else if (hasLogs()) {
+ detailsPanels.push(m(LogPanel, {}));
+ }
+
+ const wasShowing = this.showDetailsPanel;
+ this.showDetailsPanel = detailsPanels.length > 0;
+ // Pop up details panel on first selection.
+ if (!wasShowing && this.showDetailsPanel &&
+ this.detailsHeight === DRAG_HANDLE_HEIGHT_PX) {
+ this.detailsHeight = DEFAULT_DETAILS_HEIGHT_PX;
+ }
+
+ return m(
+ '.details-content',
+ {
+ style: {
+ height: `${this.detailsHeight}px`,
+ display: this.showDetailsPanel ? null : 'none'
+ }
+ },
+ m(DragHandle, {
+ resize: (height: number) => {
+ this.detailsHeight = Math.max(height, DRAG_HANDLE_HEIGHT_PX);
+ },
+ height: this.detailsHeight,
+ }),
+ m('.details-panel-container',
+ m(PanelContainer,
+ {doesScroll: true, panels: detailsPanels, kind: 'DETAILS'})));
+ }
+}
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index 25f7991..85d1eff 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -95,6 +95,7 @@
visibleTracks = new Set<string>();
prevVisibleTracks = new Set<string>();
searchIndex = -1;
+ scrollToTrackId: undefined|string|number = undefined;
private scrollBarWidth: undefined|number = undefined;
private _omniboxState: OmniboxState = {
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index b2e7f97..032cdb5 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -28,6 +28,7 @@
dur?: number;
priority?: number;
endState?: string;
+ cpu?: number;
id?: number;
utid?: number;
wakeupTs?: number;
diff --git a/ui/src/frontend/heap_profile_panel.ts b/ui/src/frontend/heap_profile_panel.ts
index d909fa8..938a80b 100644
--- a/ui/src/frontend/heap_profile_panel.ts
+++ b/ui/src/frontend/heap_profile_panel.ts
@@ -29,9 +29,10 @@
view() {
const heapDumpInfo = globals.heapDumpDetails;
- if (heapDumpInfo && heapDumpInfo.ts && heapDumpInfo.allocated &&
- heapDumpInfo.allocatedNotFreed && heapDumpInfo.tsNs &&
- heapDumpInfo.pid) {
+ if (heapDumpInfo && heapDumpInfo.ts !== undefined &&
+ heapDumpInfo.allocated !== undefined &&
+ heapDumpInfo.allocatedNotFreed !== undefined &&
+ heapDumpInfo.tsNs !== undefined && heapDumpInfo.pid !== undefined) {
this.ts = heapDumpInfo.tsNs;
this.pid = heapDumpInfo.pid;
return m(
@@ -57,8 +58,8 @@
])],
),
m('.explanation',
- 'Heap profile support is in beta. To explore a heap profile,',
- ' download and open it in ',
+ 'Heap profile support is in beta. If you need missing features, ',
+ 'download and open it in ',
m(`a[href='https://pprof.corp.google.com']`, 'pprof'),
' (Googlers only) or ',
m(`a[href='https://www.speedscope.app']`, 'Speedscope'),
diff --git a/ui/src/frontend/slice_panel.ts b/ui/src/frontend/slice_panel.ts
index 80f5c7b..99191fe 100644
--- a/ui/src/frontend/slice_panel.ts
+++ b/ui/src/frontend/slice_panel.ts
@@ -14,6 +14,7 @@
import * as m from 'mithril';
+import {Actions} from '../common/actions';
import {drawDoubleHeadedArrow} from '../common/canvas_utils';
import {translateState} from '../common/thread_state';
import {timeToCode} from '../common/time';
@@ -21,37 +22,46 @@
import {globals} from './globals';
import {Panel, PanelSize} from './panel';
-
export class SliceDetailsPanel extends Panel {
view() {
const sliceInfo = globals.sliceDetails;
- if (!sliceInfo.utid) return;
+ if (sliceInfo.utid === undefined) return;
const threadInfo = globals.threads.get(sliceInfo.utid);
- if (threadInfo && sliceInfo.ts && sliceInfo.dur) {
+ if (threadInfo && sliceInfo.ts !== undefined &&
+ sliceInfo.dur !== undefined) {
return m(
'.details-panel',
m('.details-panel-heading', `Slice Details:`),
- m('.details-table', [m('table', [
- m('tr', m('th', `PID`), m('td', `${threadInfo.pid}`)),
- m('tr',
- m('th', `Process name`),
- m('td', `${threadInfo.procName}`)),
- m('tr', m('th', `TID`), m('td', `${threadInfo.tid}`)),
- m('tr',
- m('th', `Thread name`),
- m('td', `${threadInfo.threadName}`)),
- m('tr',
- m('th', `Start time`),
- m('td', `${timeToCode(sliceInfo.ts)}`)),
- m('tr',
- m('th', `Duration`),
- m('td', `${timeToCode(sliceInfo.dur)}`)),
- m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
- m('tr',
- m('th', `End State`),
- m('td', `${translateState(sliceInfo.endState)}`))
- ])], ));
+ m(
+ '.details-table',
+ [m('table',
+ [
+ m('tr',
+ m('th', `Process`),
+ m('td', `${threadInfo.procName} [${threadInfo.pid}]`)),
+ m('tr',
+ m('th', `Thread`),
+ m('td',
+ `${threadInfo.threadName} [${threadInfo.tid}]`,
+ m('i.material-icons',
+ {
+ onclick: () => this.goToThread(),
+ title: 'Go to thread'
+ },
+ 'call_made'))),
+ m('tr',
+ m('th', `Start time`),
+ m('td', `${timeToCode(sliceInfo.ts)}`)),
+ m('tr',
+ m('th', `Duration`),
+ m('td', `${timeToCode(sliceInfo.dur)}`)),
+ m('tr', m('th', `Prio`), m('td', `${sliceInfo.priority}`)),
+ m('tr',
+ m('th', `End State`),
+ m('td', `${translateState(sliceInfo.endState)}`))
+ ])],
+ ));
} else {
return m(
'.details-panel',
@@ -62,6 +72,41 @@
}
}
+ goToThread() {
+ const sliceInfo = globals.sliceDetails;
+ if (sliceInfo.utid === undefined) return;
+ const threadInfo = globals.threads.get(sliceInfo.utid);
+
+ if (sliceInfo.id === undefined || sliceInfo.ts === undefined ||
+ sliceInfo.dur === undefined || sliceInfo.cpu === undefined ||
+ threadInfo === undefined) {
+ return;
+ }
+ globals.makeSelection(Actions.selectThreadState({
+ utid: threadInfo.utid,
+ ts: sliceInfo.ts + globals.state.traceTime.startSec,
+ dur: sliceInfo.dur,
+ state: 'Running',
+ cpu: sliceInfo.cpu,
+ }));
+ let trackId: string|number|undefined;
+ let trackGroupId;
+ for (const track of Object.values(globals.state.tracks)) {
+ if (track.kind === 'ThreadStateTrack' &&
+ (track.config as {utid: number}).utid === threadInfo.utid) {
+ trackGroupId = track.trackGroup;
+ trackId = track.id;
+ }
+ }
+ // After the track exists in the dom, it will be scrolled to.
+ globals.frontendLocalState.scrollToTrackId = trackId;
+
+ if (trackGroupId && globals.state.trackGroups[trackGroupId].collapsed) {
+ globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId}));
+ }
+ }
+
+
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
const details = globals.sliceDetails;
// Show expanded details on the scheduling of the currently selected slice.
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index d60f4b4..1af0ef0 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -20,6 +20,7 @@
import {globals} from './globals';
import {drawGridLines} from './gridline_helper';
import {Panel, PanelSize} from './panel';
+import {verticalScrollToTrack} from './scroll_helper';
import {Track} from './track';
import {TRACK_SHELL_WIDTH} from './track_constants';
import {trackRegistry} from './track_registry';
@@ -187,6 +188,13 @@
m(TrackContent, {track: attrs.track})
]);
}
+
+ onupdate({attrs}: m.CVnode<TrackComponentAttrs>) {
+ if (globals.frontendLocalState.scrollToTrackId === attrs.trackState.id) {
+ verticalScrollToTrack(attrs.trackState.id);
+ globals.frontendLocalState.scrollToTrackId = undefined;
+ }
+ }
}
export interface TrackButtonAttrs {
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 53430c4..37c0279 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -15,25 +15,18 @@
import * as m from 'mithril';
import {Actions} from '../common/actions';
-import {LogExists, LogExistsKey} from '../common/logs';
import {QueryResponse} from '../common/queries';
import {TimeSpan} from '../common/time';
-import {ChromeSliceDetailsPanel} from './chrome_slice_panel';
import {copyToClipboard} from './clipboard';
-import {CounterDetailsPanel} from './counter_panel';
-import {DragGestureHandler} from './drag_gesture_handler';
+import {DetailsPanel} from './details_panel';
import {globals} from './globals';
-import {HeapProfileDetailsPanel} from './heap_profile_panel';
-import {LogPanel} from './logs_panel';
-import {NotesEditorPanel, NotesPanel} from './notes_panel';
+import {NotesPanel} from './notes_panel';
import {OverviewTimelinePanel} from './overview_timeline_panel';
import {createPage} from './pages';
import {PanAndZoomHandler} from './pan_and_zoom_handler';
import {Panel} from './panel';
import {AnyAttrsVnode, PanelContainer} from './panel_container';
-import {SliceDetailsPanel} from './slice_panel';
-import {ThreadStatePanel} from './thread_state_panel';
import {TickmarkPanel} from './tickmark_panel';
import {TimeAxisPanel} from './time_axis_panel';
import {computeZoom} from './time_scale';
@@ -43,17 +36,8 @@
import {TrackPanel} from './track_panel';
import {VideoPanel} from './video_panel';
-const DRAG_HANDLE_HEIGHT_PX = 28;
-const DEFAULT_DETAILS_HEIGHT_PX = 230 + DRAG_HANDLE_HEIGHT_PX;
-const UP_ICON = 'keyboard_arrow_up';
-const DOWN_ICON = 'keyboard_arrow_down';
const SIDEBAR_WIDTH = 256;
-function hasLogs(): boolean {
- const data = globals.trackDataStore.get(LogExistsKey) as LogExists;
- return data && data.exists;
-}
-
class QueryTable extends Panel {
view() {
const resp = globals.queryResults.get('command') as QueryResponse;
@@ -113,71 +97,6 @@
renderCanvas() {}
}
-interface DragHandleAttrs {
- height: number;
- resize: (height: number) => void;
-}
-
-class DragHandle implements m.ClassComponent<DragHandleAttrs> {
- private dragStartHeight = 0;
- private height = 0;
- private resize: (height: number) => void = () => {};
- private isClosed = this.height <= DRAG_HANDLE_HEIGHT_PX;
-
- oncreate({dom, attrs}: m.CVnodeDOM<DragHandleAttrs>) {
- this.resize = attrs.resize;
- this.height = attrs.height;
- this.isClosed = this.height <= DRAG_HANDLE_HEIGHT_PX;
- const elem = dom as HTMLElement;
- new DragGestureHandler(
- elem,
- this.onDrag.bind(this),
- this.onDragStart.bind(this),
- this.onDragEnd.bind(this));
- }
-
- onupdate({attrs}: m.CVnodeDOM<DragHandleAttrs>) {
- this.resize = attrs.resize;
- this.height = attrs.height;
- this.isClosed = this.height <= DRAG_HANDLE_HEIGHT_PX;
- }
-
- onDrag(_x: number, y: number) {
- const newHeight = this.dragStartHeight + (DRAG_HANDLE_HEIGHT_PX / 2) - y;
- this.isClosed = Math.floor(newHeight) <= DRAG_HANDLE_HEIGHT_PX;
- this.resize(Math.floor(newHeight));
- globals.rafScheduler.scheduleFullRedraw();
- }
-
- onDragStart(_x: number, _y: number) {
- this.dragStartHeight = this.height;
- }
-
- onDragEnd() {}
-
- view() {
- const icon = this.isClosed ? UP_ICON : DOWN_ICON;
- const title = this.isClosed ? 'Show panel' : 'Hide panel';
- return m(
- '.handle',
- m('.handle-title', 'Current Selection'),
- m('i.material-icons',
- {
- onclick: () => {
- if (this.height === DRAG_HANDLE_HEIGHT_PX) {
- this.isClosed = false;
- this.resize(DEFAULT_DETAILS_HEIGHT_PX);
- } else {
- this.isClosed = true;
- this.resize(DRAG_HANDLE_HEIGHT_PX);
- }
- globals.rafScheduler.scheduleFullRedraw();
- },
- title
- },
- icon));
- }
-}
// Checks if the mousePos is within 3px of the start or end of the
// current selected time range.
@@ -206,9 +125,6 @@
class TraceViewer implements m.ClassComponent {
private onResize: () => void = () => {};
private zoomContent?: PanAndZoomHandler;
- private detailsHeight = DRAG_HANDLE_HEIGHT_PX;
- // Used to set details panel to default height on selection.
- private showDetailsPanel = true;
// Used to prevent global deselection if a pan/drag select occurred.
private keepCurrentSelection = false;
@@ -327,57 +243,6 @@
}
scrollingPanels.unshift(m(QueryTable, {key: 'query'}));
- const detailsPanels: AnyAttrsVnode[] = [];
- const curSelection = globals.state.currentSelection;
- if (curSelection) {
- switch (curSelection.kind) {
- case 'NOTE':
- detailsPanels.push(m(NotesEditorPanel, {
- key: 'notes',
- id: curSelection.id,
- }));
- break;
- case 'SLICE':
- detailsPanels.push(m(SliceDetailsPanel, {
- key: 'slice',
- }));
- break;
- case 'COUNTER':
- detailsPanels.push(m(CounterDetailsPanel, {
- key: 'counter',
- }));
- break;
- case 'HEAP_PROFILE':
- detailsPanels.push(m(HeapProfileDetailsPanel, {key: 'heap_profile'}));
- break;
- case 'CHROME_SLICE':
- detailsPanels.push(m(ChromeSliceDetailsPanel));
- break;
- case 'THREAD_STATE':
- detailsPanels.push(m(ThreadStatePanel, {
- key: 'thread_state',
- ts: curSelection.ts,
- dur: curSelection.dur,
- utid: curSelection.utid,
- state: curSelection.state,
- cpu: curSelection.cpu
- }));
- break;
- default:
- break;
- }
- } else if (hasLogs()) {
- detailsPanels.push(m(LogPanel, {}));
- }
-
- const wasShowing = this.showDetailsPanel;
- this.showDetailsPanel = detailsPanels.length > 0;
- // Pop up details panel on first selection.
- if (!wasShowing && this.showDetailsPanel &&
- this.detailsHeight === DRAG_HANDLE_HEIGHT_PX) {
- this.detailsHeight = DEFAULT_DETAILS_HEIGHT_PX;
- }
-
return m(
'.page',
m('.split-panel',
@@ -414,22 +279,7 @@
(globals.state.videoEnabled && globals.state.video != null) ?
m(VideoPanel) :
null)),
- m('.details-content',
- {
- style: {
- height: `${this.detailsHeight}px`,
- display: this.showDetailsPanel ? null : 'none'
- }
- },
- m(DragHandle, {
- resize: (height: number) => {
- this.detailsHeight = Math.max(height, DRAG_HANDLE_HEIGHT_PX);
- },
- height: this.detailsHeight,
- }),
- m('.details-panel-container',
- m(PanelContainer,
- {doesScroll: true, panels: detailsPanels, kind: 'DETAILS'}))));
+ m(DetailsPanel));
}
}
diff --git a/ui/src/tracks/process_summary/controller.ts b/ui/src/tracks/process_summary/controller.ts
index a7a73aa..65835c1 100644
--- a/ui/src/tracks/process_summary/controller.ts
+++ b/ui/src/tracks/process_summary/controller.ts
@@ -64,7 +64,8 @@
}
// |resolution| is in s/px we want # ns for 10px window:
- const bucketSizeNs = Math.round(resolution * 10 * 1e9);
+ // Max value with 1 so we don't end up with resolution 0.
+ const bucketSizeNs = Math.max(1, Math.round(resolution * 10 * 1e9));
const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs;
const windowDurNs = Math.max(1, endNs - windowStartNs);