TraceProcessor: add support for complete JSON events
Will require some follow-up clean ups, but in the meanwhile
brings us into a nice state w.r.t. digesting legacy traces.
This CL:
- Extracts thread and process names from metadata events.
- Creates a full stack of slice using both duration and complete events
(TRACE_EVENT_BEGIN/END(...) and TRACE_EVENT(...))
- Exposes pids and tid in the process and thread vtables.
- Refactors the ProcessTracker to support identifying threads.
by the pid+tid tuple.
- Improves the hashing of the string pool using 32-bit FNV-1a.
See https://ghostbin.com/paste/bpvq7 for a sample SQL query.
Change-Id: I567b102e14770e521f60650bcab45cb0f24fb76c
diff --git a/src/trace_processor/json_trace_parser.cc b/src/trace_processor/json_trace_parser.cc
index 6c5cbcc9..47c89f2 100644
--- a/src/trace_processor/json_trace_parser.cc
+++ b/src/trace_processor/json_trace_parser.cc
@@ -18,6 +18,8 @@
#include <json/reader.h>
#include <json/value.h>
+
+#include <limits>
#include <string>
#include "perfetto/base/build_config.h"
@@ -36,7 +38,7 @@
namespace trace_processor {
namespace {
-const uint32_t kChunkSize = 65536;
+const uint32_t kChunkSize = 1024 * 512;
// Parses at most one JSON dictionary and returns a pointer to the end of it,
// or nullptr if no dict could be detected.
@@ -74,6 +76,7 @@
}
return nullptr;
}
+
} // namespace
// static
@@ -127,27 +130,104 @@
StringId cat_id = storage->InternString(cat, strlen(cat));
StringId name_id = storage->InternString(name, strlen(name));
UniqueTid utid = procs->UpdateThread(tid, pid);
- std::vector<Slice>& stack = threads_[utid].stack;
+ SlicesStack& stack = threads_[utid];
+
+ auto add_slice = [slices, &stack, utid, cat_id,
+ name_id](const Slice& slice) {
+ if (stack.size() >= std::numeric_limits<uint8_t>::max())
+ return;
+ const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1;
+ uint64_t parent_stack_id, stack_id;
+ std::tie(parent_stack_id, stack_id) = GetStackHashes(stack);
+ slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid,
+ cat_id, name_id, depth, stack_id, parent_stack_id);
+ };
switch (phase) {
- case 'B': // TRACE_EVENT_BEGIN
- stack.emplace_back(Slice{cat_id, name_id, ts});
+ case 'B': { // TRACE_EVENT_BEGIN.
+ MaybeCloseStack(ts, stack);
+ stack.emplace_back(Slice{cat_id, name_id, ts, 0});
break;
- case 'E': // TRACE_EVENT_END
- PERFETTO_CHECK(!stack.empty() && stack.back().cat_id == cat_id &&
- stack.back().name_id == name_id);
+ }
+ case 'E': { // TRACE_EVENT_END.
+ PERFETTO_CHECK(!stack.empty());
+ MaybeCloseStack(ts, stack);
+ PERFETTO_CHECK(stack.back().cat_id == cat_id);
+ PERFETTO_CHECK(stack.back().name_id == name_id);
Slice& slice = stack.back();
- if (stack.size() < 0xff) {
- slices->AddSlice(slice.start_ts, ts - slice.start_ts, utid, cat_id,
- name_id, static_cast<uint8_t>(stack.size()));
- }
+ slice.end_ts = slice.start_ts;
+ add_slice(slice);
stack.pop_back();
break;
+ }
+ case 'X': { // TRACE_EVENT (scoped event).
+ MaybeCloseStack(ts, stack);
+ uint64_t end_ts = ts + value["dur"].asUInt();
+ stack.emplace_back(Slice{cat_id, name_id, ts, end_ts});
+ Slice& slice = stack.back();
+ add_slice(slice);
+ break;
+ }
+ case 'M': { // Metadata events (process and thread names).
+ if (strcmp(value["name"].asCString(), "thread_name") == 0) {
+ const char* thread_name = value["args"]["name"].asCString();
+ procs->UpdateThreadName(tid, pid, thread_name, strlen(thread_name));
+ break;
+ }
+ if (strcmp(value["name"].asCString(), "process_name") == 0) {
+ const char* proc_name = value["args"]["name"].asCString();
+ procs->UpdateProcess(pid, proc_name, strlen(proc_name));
+ break;
+ }
+ }
}
+ // TODO(primiano): auto-close B slices left open at the end.
}
offset_ += static_cast<uint64_t>(next - buf);
return next > buf;
}
+void JsonTraceParser::MaybeCloseStack(uint64_t ts, SlicesStack& stack) {
+ bool check_only = false;
+ for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) {
+ const Slice& slice = stack[size_t(i)];
+ if (slice.end_ts == 0) {
+ check_only = true;
+ }
+
+ if (check_only) {
+ PERFETTO_DCHECK(ts >= slice.start_ts);
+ PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts);
+ continue;
+ }
+
+ if (slice.end_ts <= ts) {
+ stack.pop_back();
+ }
+ }
+}
+
+// Returns <parent_stack_id, stack_id>, where
+// |parent_stack_id| == hash(stack_id - last slice).
+std::tuple<uint64_t, uint64_t> JsonTraceParser::GetStackHashes(
+ const SlicesStack& stack) {
+ PERFETTO_DCHECK(!stack.empty());
+ std::string s;
+ s.reserve(stack.size() * sizeof(uint64_t) * 2);
+ constexpr uint64_t kMask = uint64_t(-1) >> 1;
+ uint64_t parent_stack_id = 0;
+ for (size_t i = 0; i < stack.size(); i++) {
+ if (i == stack.size() - 1)
+ parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0;
+ const Slice& slice = stack[i];
+ s.append(reinterpret_cast<const char*>(&slice.cat_id),
+ sizeof(slice.cat_id));
+ s.append(reinterpret_cast<const char*>(&slice.name_id),
+ sizeof(slice.name_id));
+ }
+ uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask;
+ return std::make_tuple(parent_stack_id, stack_id);
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/json_trace_parser.h b/src/trace_processor/json_trace_parser.h
index 98fb6ad..8fc5ef3 100644
--- a/src/trace_processor/json_trace_parser.h
+++ b/src/trace_processor/json_trace_parser.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <memory>
+#include <tuple>
#include <unordered_map>
#include "src/trace_processor/trace_parser.h"
@@ -51,16 +52,19 @@
StringId cat_id;
StringId name_id;
uint64_t start_ts;
+ uint64_t end_ts; // Only for complete events (scoped TRACE_EVENT macros).
};
- struct ThreadState {
- std::vector<Slice> stack;
- };
+ using SlicesStack = std::vector<Slice>;
+
+ static inline void MaybeCloseStack(uint64_t end_ts, SlicesStack&);
+ static inline std::tuple<uint64_t, uint64_t> GetStackHashes(
+ const SlicesStack&);
BlobReader* const reader_;
TraceProcessorContext* const context_;
uint64_t offset_ = 0;
std::unique_ptr<char[]> buffer_;
- std::unordered_map<UniqueTid, ThreadState> threads_;
+ std::unordered_map<UniqueTid, SlicesStack> threads_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/process_table.cc b/src/trace_processor/process_table.cc
index dffd4af..471abd0 100644
--- a/src/trace_processor/process_table.cc
+++ b/src/trace_processor/process_table.cc
@@ -36,6 +36,7 @@
"CREATE TABLE process("
"upid UNSIGNED INT, "
"name TEXT, "
+ "pid UNSIGNED INT, "
"PRIMARY KEY(upid)"
") WITHOUT ROWID;");
}
@@ -68,6 +69,11 @@
static_cast<int>(name.length()), nullptr);
break;
}
+ case Column::kPid: {
+ const auto& process = storage_->GetProcess(upid_filter_.current);
+ sqlite3_result_int64(context, process.pid);
+ break;
+ }
default:
PERFETTO_FATAL("Unknown column %d", N);
break;
diff --git a/src/trace_processor/process_table.h b/src/trace_processor/process_table.h
index 3799e28..64bd1f5 100644
--- a/src/trace_processor/process_table.h
+++ b/src/trace_processor/process_table.h
@@ -30,7 +30,7 @@
// their details (only name at the moment).
class ProcessTable : public Table {
public:
- enum Column { kUpid = 0, kName = 1 };
+ enum Column { kUpid = 0, kName = 1, kPid = 2 };
static void RegisterTable(sqlite3* db, const TraceStorage* storage);
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc
index 4cdef78..45df50e 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/process_tracker.cc
@@ -39,78 +39,95 @@
}
// If none exist, assign a new utid and store it.
- UniqueTid new_utid = context_->storage->AddEmptyThread();
+ UniqueTid new_utid = context_->storage->AddEmptyThread(tid);
TraceStorage::Thread* thread = context_->storage->GetMutableThread(new_utid);
thread->name_id = thread_name_id;
- thread->start_ns = timestamp;
+ if (timestamp)
+ thread->start_ns = timestamp;
tids_.emplace(tid, new_utid);
return new_utid;
};
-UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t tgid) {
+void ProcessTracker::UpdateThreadName(uint32_t tid,
+ uint32_t pid,
+ const char* name,
+ size_t name_len) {
+ UniqueTid utid = UpdateThread(tid, pid);
+ auto* thread = context_->storage->GetMutableThread(utid);
+ auto name_id = context_->storage->InternString(name, name_len);
+ thread->name_id = name_id;
+}
+
+UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t pid) {
auto tids_pair = tids_.equal_range(tid);
- // TODO(b/110409911): Remove once invalidation of threads is implemented.
- PERFETTO_DCHECK(std::distance(tids_pair.first, tids_pair.second) <= 1);
-
- UniqueTid utid = 0;
- // Find matching thread for tid or create new one.
+ // Try looking for a thread that matches both tid and thread group id (pid).
TraceStorage::Thread* thread = nullptr;
- if (tids_pair.first == tids_pair.second) {
- utid = context_->storage->AddEmptyThread();
+ UniqueTid utid = 0;
+ for (auto it = tids_pair.first; it != tids_pair.second; it++) {
+ UniqueTid iter_utid = it->second;
+ auto* iter_thread = context_->storage->GetMutableThread(iter_utid);
+ if (iter_thread->upid == 0) {
+ // We haven't discovered the parent process for the thread. Assign it
+ // now and use this thread.
+ thread = iter_thread;
+ utid = iter_utid;
+ break;
+ }
+ const auto& iter_process = context_->storage->GetProcess(iter_thread->upid);
+ if (iter_process.pid == pid) {
+ // We found a thread that matches both the tid and its parent pid.
+ thread = iter_thread;
+ utid = iter_utid;
+ break;
+ }
+ } // for(tids).
+
+ // If no matching thread was found, create a new one.
+ if (thread == nullptr) {
+ utid = context_->storage->AddEmptyThread(tid);
tids_.emplace(tid, utid);
thread = context_->storage->GetMutableThread(utid);
- } else {
- utid = tids_pair.first->second;
- thread = context_->storage->GetMutableThread(utid);
}
- // Find matching upid for tgid or create new one.
- if (thread->upid == 0) {
- auto pids_pair = pids_.equal_range(tgid);
+ // Find matching process or create new one.
+ if (thread->upid == 0) // Not set, upid == 0 is invalid.
+ thread->upid = GetOrCreateProcess(pid, thread->start_ns);
- // TODO(b/110409911): Remove once invalidation of threads is implemented.
- PERFETTO_DCHECK(std::distance(pids_pair.first, pids_pair.second) <= 1);
-
- TraceStorage::Process* process = nullptr;
- if (pids_pair.first == pids_pair.second) {
- UniquePid new_upid = context_->storage->AddEmptyProcess();
- pids_.emplace(tgid, new_upid);
- process = context_->storage->GetMutableProcess(new_upid);
- thread->upid = new_upid;
- } else {
- process = context_->storage->GetMutableProcess(pids_pair.first->second);
- thread->upid = pids_pair.first->second;
- }
- if (process->start_ns == 0)
- process->start_ns = thread->start_ns;
- }
return utid;
}
UniquePid ProcessTracker::UpdateProcess(uint32_t pid,
const char* process_name,
size_t process_name_len) {
- auto pids_pair = pids_.equal_range(pid);
auto proc_name_id =
context_->storage->InternString(process_name, process_name_len);
+ UniquePid upid = GetOrCreateProcess(pid, 0 /* start_ns */);
+ auto* process = context_->storage->GetMutableProcess(upid);
+ process->name_id = proc_name_id;
+ return upid;
+}
- // If a upid exists for the pid, find it and update the name.
- if (pids_pair.first != pids_pair.second) {
- auto prev_upid = std::prev(pids_pair.second)->second;
- TraceStorage::Process* process =
- context_->storage->GetMutableProcess(prev_upid);
- process->name_id = proc_name_id;
- return prev_upid;
+UniquePid ProcessTracker::GetOrCreateProcess(uint32_t pid, uint64_t start_ns) {
+ auto pids_pair = pids_.equal_range(pid);
+
+ UniquePid upid = 0;
+ for (auto it = pids_pair.first; it != pids_pair.second; it++) {
+ if (it->first == pid) {
+ upid = it->second;
+ break;
+ }
}
- // Create a new upid if there isn't one for that pid.
- UniquePid new_upid = context_->storage->AddEmptyProcess();
- TraceStorage::Process* process =
- context_->storage->GetMutableProcess(new_upid);
- pids_.emplace(pid, new_upid);
- process->name_id = proc_name_id;
- return new_upid;
+ if (upid == 0) {
+ upid = context_->storage->AddEmptyProcess(pid);
+ pids_.emplace(pid, upid);
+ }
+
+ auto* process = context_->storage->GetMutableProcess(upid);
+ if (process->start_ns == 0)
+ process->start_ns = start_ns;
+ return upid;
}
} // namespace trace_processor
diff --git a/src/trace_processor/process_tracker.h b/src/trace_processor/process_tracker.h
index b087407..651f962 100644
--- a/src/trace_processor/process_tracker.h
+++ b/src/trace_processor/process_tracker.h
@@ -56,6 +56,12 @@
// Virtual for testing.
virtual UniqueTid UpdateThread(uint32_t tid, uint32_t tgid);
+ // Sets the name of the thread identified by the tuple (tid,pid).
+ void UpdateThreadName(uint32_t tid,
+ uint32_t pid,
+ const char* name,
+ size_t name_len);
+
// Called when a process is seen in a process tree. Retrieves the UniquePid
// for that pid or assigns a new one.
// Virtual for testing.
@@ -75,16 +81,18 @@
return tids_.equal_range(tid);
}
+ UniquePid GetOrCreateProcess(uint32_t pid, uint64_t start_ns);
+
private:
TraceProcessorContext* const context_;
// Each tid can have multiple UniqueTid entries, a new UniqueTid is assigned
// each time a thread is seen in the trace.
- std::multimap<uint32_t, UniqueTid> tids_;
+ std::multimap<uint32_t /* tid */, UniqueTid> tids_;
// Each pid can have multiple UniquePid entries, a new UniquePid is assigned
// each time a process is seen in the trace.
- std::multimap<uint32_t, UniquePid> pids_;
+ std::multimap<uint32_t /* pid (aka tgid) */, UniquePid> pids_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/process_tracker_unittest.cc b/src/trace_processor/process_tracker_unittest.cc
index b4b3d6d..2e0b1dd 100644
--- a/src/trace_processor/process_tracker_unittest.cc
+++ b/src/trace_processor/process_tracker_unittest.cc
@@ -74,26 +74,27 @@
TEST_F(ProcessTrackerTest, UpdateThreadMatch) {
uint32_t cpu = 3;
uint64_t timestamp = 100;
- uint32_t pid_1 = 1;
uint32_t prev_state = 32;
static const char kCommProc1[] = "process1";
static const char kCommProc2[] = "process2";
- uint32_t pid_2 = 4;
- context.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+ context.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
kCommProc1, sizeof(kCommProc1) - 1,
- pid_2);
- context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
- kCommProc2, sizeof(kCommProc2) - 1,
- pid_1);
+ /*tid=*/4);
+ context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+ prev_state, kCommProc2,
+ sizeof(kCommProc2) - 1,
+ /*tid=*/1);
- context.process_tracker->UpdateProcess(2, "test", 4);
- context.process_tracker->UpdateThread(1, 2);
+ context.process_tracker->UpdateProcess(2, "test", strlen("test"));
+ context.process_tracker->UpdateThread(4, 2);
- TraceStorage::Thread thread = context.storage->GetThread(1);
- TraceStorage::Process process = context.storage->GetProcess(1);
+ TraceStorage::Thread thread = context.storage->GetThread(/*utid=*/1);
+ TraceStorage::Process process = context.storage->GetProcess(/*utid=*/1);
+ ASSERT_EQ(thread.tid, 4);
ASSERT_EQ(thread.upid, 1);
+ ASSERT_EQ(process.pid, 2);
ASSERT_EQ(process.start_ns, timestamp);
}
diff --git a/src/trace_processor/sched_tracker.cc b/src/trace_processor/sched_tracker.cc
index 16d1508..1c9984f 100644
--- a/src/trace_processor/sched_tracker.cc
+++ b/src/trace_processor/sched_tracker.cc
@@ -39,9 +39,10 @@
// slice.
if (prev->valid() && prev->next_pid != 0 /* Idle process (swapper/N) */) {
uint64_t duration = timestamp - prev->timestamp;
-
+ StringId prev_thread_name_id =
+ context_->storage->InternString(prev_comm, prev_comm_len);
UniqueTid utid = context_->process_tracker->UpdateThread(
- prev->timestamp, prev->prev_pid, prev->prev_thread_name_id);
+ prev->timestamp, prev->next_pid /* == prev_pid */, prev_thread_name_id);
context_->storage->AddSliceToCpu(cpu, prev->timestamp, duration, utid);
}
@@ -55,8 +56,6 @@
prev->timestamp = timestamp;
prev->prev_pid = prev_pid;
prev->prev_state = prev_state;
- prev->prev_thread_name_id =
- context_->storage->InternString(prev_comm, prev_comm_len);
prev->next_pid = next_pid;
};
diff --git a/src/trace_processor/sched_tracker.h b/src/trace_processor/sched_tracker.h
index 042408d..c0f8f0a 100644
--- a/src/trace_processor/sched_tracker.h
+++ b/src/trace_processor/sched_tracker.h
@@ -40,7 +40,6 @@
struct SchedSwitchEvent {
uint64_t timestamp = 0;
- StringId prev_thread_name_id = 0;
uint32_t prev_pid = 0;
uint32_t prev_state = 0;
uint32_t next_pid = 0;
diff --git a/src/trace_processor/sched_tracker_unittest.cc b/src/trace_processor/sched_tracker_unittest.cc
index 96b5bb5..0344446 100644
--- a/src/trace_processor/sched_tracker_unittest.cc
+++ b/src/trace_processor/sched_tracker_unittest.cc
@@ -65,37 +65,44 @@
ASSERT_EQ(context.storage->GetThread(1).start_ns, timestamp);
ASSERT_EQ(std::string(context.storage->GetString(
context.storage->GetThread(1).name_id)),
- "process1");
+ kCommProc2);
ASSERT_EQ(context.storage->SlicesForCpu(cpu).utids().front(), 1);
}
TEST_F(SchedTrackerTest, InsertThirdSched_SameThread) {
uint32_t cpu = 3;
uint64_t timestamp = 100;
- uint32_t pid_1 = 2;
uint32_t prev_state = 32;
static const char kCommProc1[] = "process1";
static const char kCommProc2[] = "process2";
- uint32_t pid_2 = 4;
const auto& timestamps = context.storage->SlicesForCpu(cpu).start_ns();
- context.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+ context.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/4, prev_state,
kCommProc1, sizeof(kCommProc1) - 1,
- pid_1);
+ /*tid=*/2);
ASSERT_EQ(timestamps.size(), 0);
- context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_1, prev_state,
- kCommProc1, sizeof(kCommProc1) - 1,
- pid_2);
- context.sched_tracker->PushSchedSwitch(cpu, timestamp + 2, pid_2, prev_state,
- kCommProc2, sizeof(kCommProc2) - 1,
- pid_1);
+ context.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/2,
+ prev_state, kCommProc1,
+ sizeof(kCommProc1) - 1,
+ /*tid=*/4);
+ context.sched_tracker->PushSchedSwitch(cpu, timestamp + 11, /*tid=*/4,
+ prev_state, kCommProc2,
+ sizeof(kCommProc2) - 1,
+ /*tid=*/2);
+ context.sched_tracker->PushSchedSwitch(cpu, timestamp + 31, /*tid=*/4,
+ prev_state, kCommProc1,
+ sizeof(kCommProc1) - 1,
+ /*tid=*/2);
- ASSERT_EQ(timestamps.size(), 2ul);
+ ASSERT_EQ(timestamps.size(), 3ul);
ASSERT_EQ(timestamps[0], timestamp);
ASSERT_EQ(context.storage->GetThread(1).start_ns, timestamp);
+ ASSERT_EQ(context.storage->SlicesForCpu(cpu).durations().at(0), 1u);
+ ASSERT_EQ(context.storage->SlicesForCpu(cpu).durations().at(1), 11u - 1u);
+ ASSERT_EQ(context.storage->SlicesForCpu(cpu).durations().at(2), 31u - 11u);
ASSERT_EQ(context.storage->SlicesForCpu(cpu).utids().at(0),
- context.storage->SlicesForCpu(cpu).utids().at(1));
+ context.storage->SlicesForCpu(cpu).utids().at(2));
}
} // namespace
diff --git a/src/trace_processor/slice_table.cc b/src/trace_processor/slice_table.cc
index a772eac..0a9de2b 100644
--- a/src/trace_processor/slice_table.cc
+++ b/src/trace_processor/slice_table.cc
@@ -39,6 +39,8 @@
"cat STRING,"
"name STRING,"
"depth INT,"
+ "stack_id UNSIGNED BIG INT,"
+ "parent_stack_id UNSIGNED BIG INT,"
"PRIMARY KEY(utid, ts, depth)"
") WITHOUT ROWID;");
}
@@ -103,6 +105,14 @@
sqlite3_result_int64(context,
static_cast<sqlite3_int64>(slices.depths()[row_]));
break;
+ case Column::kStackId:
+ sqlite3_result_int64(
+ context, static_cast<sqlite3_int64>(slices.stack_ids()[row_]));
+ break;
+ case Column::kParentStackId:
+ sqlite3_result_int64(
+ context, static_cast<sqlite3_int64>(slices.parent_stack_ids()[row_]));
+ break;
}
return SQLITE_OK;
}
diff --git a/src/trace_processor/slice_table.h b/src/trace_processor/slice_table.h
index 32e120d..b821ec8 100644
--- a/src/trace_processor/slice_table.h
+++ b/src/trace_processor/slice_table.h
@@ -43,6 +43,8 @@
kCategory = 3,
kName = 4,
kDepth = 5,
+ kStackId = 6,
+ kParentStackId = 7,
};
SliceTable(const TraceStorage* storage);
diff --git a/src/trace_processor/thread_table.cc b/src/trace_processor/thread_table.cc
index e3a56cd..040f99d 100644
--- a/src/trace_processor/thread_table.cc
+++ b/src/trace_processor/thread_table.cc
@@ -37,6 +37,7 @@
"utid UNSIGNED INT, "
"upid UNSIGNED INT, "
"name TEXT, "
+ "tid UNSIGNED INT, "
"PRIMARY KEY(utid)"
") WITHOUT ROWID;");
}
@@ -73,6 +74,10 @@
static_cast<int>(name.length()), nullptr);
break;
}
+ case Column::kTid: {
+ sqlite3_result_int64(context, thread.tid);
+ break;
+ }
default: {
PERFETTO_FATAL("Unknown column %d", N);
break;
diff --git a/src/trace_processor/thread_table.h b/src/trace_processor/thread_table.h
index cbfe908..d5b3609 100644
--- a/src/trace_processor/thread_table.h
+++ b/src/trace_processor/thread_table.h
@@ -30,7 +30,7 @@
// the metadata for those processes.
class ThreadTable : public Table {
public:
- enum Column { kUtid = 0, kUpid = 1, kName = 2 };
+ enum Column { kUtid = 0, kUpid = 1, kName = 2, kTid = 3 };
static void RegisterTable(sqlite3* db, const TraceStorage* storage);
diff --git a/src/trace_processor/thread_table_unittest.cc b/src/trace_processor/thread_table_unittest.cc
index c0b4225..19a4201 100644
--- a/src/trace_processor/thread_table_unittest.cc
+++ b/src/trace_processor/thread_table_unittest.cc
@@ -67,27 +67,27 @@
TEST_F(ThreadTableUnittest, Select) {
uint32_t cpu = 3;
uint64_t timestamp = 100;
- uint32_t pid_1 = 1;
uint32_t prev_state = 32;
static const char kCommProc1[] = "thread1";
static const char kCommProc2[] = "thread2";
- uint32_t pid_2 = 4;
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+ context_.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
kCommProc1, sizeof(kCommProc1) - 1,
- pid_2);
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
- kCommProc2, sizeof(kCommProc2) - 1,
- pid_1);
+ /*tid=*/4);
+ context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+ prev_state, kCommProc2,
+ sizeof(kCommProc2) - 1,
+ /*tid=*/1);
- context_.process_tracker->UpdateProcess(2, "test", 4);
- context_.process_tracker->UpdateThread(1, 2);
- PrepareValidStatement("SELECT utid, upid, name FROM thread");
+ context_.process_tracker->UpdateProcess(2, "test", strlen("test"));
+ context_.process_tracker->UpdateThread(4 /*tid*/, 2 /*pid*/);
+ PrepareValidStatement("SELECT utid, upid, tid, name FROM thread");
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1 /* upid */);
- ASSERT_STREQ(GetColumnAsText(2), kCommProc1);
+ ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 4 /* tid */);
+ ASSERT_STREQ(GetColumnAsText(3), kCommProc2);
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
}
@@ -95,31 +95,33 @@
TEST_F(ThreadTableUnittest, SelectWhere) {
uint32_t cpu = 3;
uint64_t timestamp = 100;
- uint32_t pid_1 = 1;
uint32_t prev_state = 32;
static const char kCommProc1[] = "thread1";
static const char kCommProc2[] = "thread2";
- uint32_t pid_2 = 4;
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+ context_.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
kCommProc1, sizeof(kCommProc1) - 1,
- pid_2);
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
- kCommProc2, sizeof(kCommProc2) - 1,
- pid_1);
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 2, pid_1, prev_state,
- kCommProc1, sizeof(kCommProc1) - 1,
- pid_2);
+ /*tid=*/4);
+ context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+ prev_state, kCommProc2,
+ sizeof(kCommProc2) - 1,
+ /*tid=*/1);
+ context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 2, /*tid=*/1,
+ prev_state, kCommProc1,
+ sizeof(kCommProc1) - 1,
+ /*tid=*/4);
- context_.process_tracker->UpdateProcess(2, "test", 4);
- context_.process_tracker->UpdateThread(1, 2);
- context_.process_tracker->UpdateThread(2, 2);
- PrepareValidStatement("SELECT utid, upid, name FROM thread where utid = 1");
+ context_.process_tracker->UpdateProcess(2, "test", strlen("test"));
+ context_.process_tracker->UpdateThread(4 /*tid*/, 2 /*pid*/);
+ context_.process_tracker->UpdateThread(1 /*tid*/, 2 /*pid*/);
+ PrepareValidStatement(
+ "SELECT utid, upid, tid, name FROM thread where tid = 4");
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1 /* upid */);
- ASSERT_STREQ(GetColumnAsText(2), kCommProc1);
+ ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 4 /* tid */);
+ ASSERT_STREQ(GetColumnAsText(3), kCommProc2);
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
}
@@ -127,32 +129,36 @@
TEST_F(ThreadTableUnittest, JoinWithProcess) {
uint32_t cpu = 3;
uint64_t timestamp = 100;
- uint32_t pid_1 = 1;
uint32_t prev_state = 32;
static const char kCommProc1[] = "thread1";
static const char kCommProc2[] = "thread2";
- uint32_t pid_2 = 4;
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
+ context_.sched_tracker->PushSchedSwitch(cpu, timestamp, /*tid=*/1, prev_state,
kCommProc1, sizeof(kCommProc1) - 1,
- pid_2);
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, pid_2, prev_state,
- kCommProc2, sizeof(kCommProc2) - 1,
- pid_1);
+ /*tid=*/4);
+ context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 1, /*tid=*/4,
+ prev_state, kCommProc2,
+ sizeof(kCommProc2) - 1,
+ /*tid=*/1);
- context_.process_tracker->UpdateProcess(2, "test", 4);
- context_.process_tracker->UpdateProcess(3, "test1", 5);
- context_.process_tracker->UpdateThread(1, 2);
+ // Also create a process for which we haven't seen any thread.
+ context_.process_tracker->UpdateProcess(3, "test2", strlen("test2"));
+
+ context_.process_tracker->UpdateProcess(2, "test1", strlen("test1"));
+ context_.process_tracker->UpdateThread(4, 2);
+
PrepareValidStatement(
- "SELECT utid, thread.name, process.upid, process.name FROM thread INNER "
- "JOIN process USING (upid)");
+ "SELECT utid, thread.tid, thread.name, process.upid, process.pid, "
+ "process.name FROM thread INNER JOIN process USING (upid)");
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
ASSERT_EQ(sqlite3_column_int(*stmt_, 0), 1 /* utid */);
- ASSERT_STREQ(GetColumnAsText(1), kCommProc1);
+ ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 4 /* tid */);
+ ASSERT_STREQ(GetColumnAsText(2), kCommProc2);
- ASSERT_EQ(sqlite3_column_int(*stmt_, 2), 1 /* upid */);
- ASSERT_STREQ(GetColumnAsText(3), "test");
+ ASSERT_EQ(sqlite3_column_int(*stmt_, 3), 2 /* upid */);
+ ASSERT_EQ(sqlite3_column_int(*stmt_, 4), 2 /* pid */);
+ ASSERT_STREQ(GetColumnAsText(5), "test1");
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
}
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index eda0116..5b535a0 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -67,14 +67,17 @@
for (int c = 0; c < res.columns_size(); c++) {
switch (res.column_descriptors(c).type()) {
case protos::RawQueryResult_ColumnDesc_Type_STRING:
- printf("%20s ", res.columns(c).string_values(r).c_str());
+ printf("%-20.20s ", res.columns(c).string_values(r).c_str());
break;
case protos::RawQueryResult_ColumnDesc_Type_DOUBLE:
printf("%20f ", res.columns(c).double_values(r));
break;
- case protos::RawQueryResult_ColumnDesc_Type_LONG:
- printf("%20lld ", res.columns(c).long_values(r));
+ case protos::RawQueryResult_ColumnDesc_Type_LONG: {
+ auto value = res.columns(c).long_values(r);
+ printf((value < 0xffffffll) ? "%20lld " : "%20llx ", value);
+
break;
+ }
}
}
printf("\n");
diff --git a/src/trace_processor/trace_storage.cc b/src/trace_processor/trace_storage.cc
index e6d0f55..38cb414 100644
--- a/src/trace_processor/trace_storage.cc
+++ b/src/trace_processor/trace_storage.cc
@@ -23,8 +23,11 @@
TraceStorage::TraceStorage() {
// Upid/utid 0 is reserved for invalid processes/threads.
- unique_processes_.emplace_back();
- unique_threads_.emplace_back();
+ unique_processes_.emplace_back(0);
+ unique_threads_.emplace_back(0);
+
+ // Reserve string ID 0 for the empty string.
+ InternString("", 0);
}
TraceStorage::~TraceStorage() {}
@@ -37,9 +40,10 @@
};
StringId TraceStorage::InternString(const char* data, size_t length) {
- uint32_t hash = 0;
+ uint32_t hash = 0x811c9dc5; // FNV-1a-32 offset basis.
for (size_t i = 0; i < length; ++i) {
- hash = static_cast<uint32_t>(data[i]) + (hash * 31);
+ hash ^= static_cast<decltype(hash)>(data[i]);
+ hash *= 16777619; // FNV-1a-32 prime.
}
auto id_it = string_index_.find(hash);
if (id_it != string_index_.end()) {
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 952e2c0..9613ca6 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -53,24 +53,27 @@
constexpr static size_t kMaxCpus = 128;
-
struct Stats {
uint64_t mismatched_sched_switch_tids_ = 0;
};
// Information about a unique process seen in a trace.
struct Process {
+ explicit Process(uint32_t p) : pid(p) {}
uint64_t start_ns = 0;
uint64_t end_ns = 0;
StringId name_id = 0;
+ uint32_t pid = 0;
};
// Information about a unique thread seen in a trace.
struct Thread {
+ explicit Thread(uint32_t t) : tid(t) {}
uint64_t start_ns = 0;
uint64_t end_ns = 0;
StringId name_id = 0;
UniquePid upid = 0;
+ uint32_t tid = 0;
};
class SlicesPerCpu {
@@ -106,13 +109,17 @@
UniqueTid utid,
StringId cat,
StringId name,
- uint8_t depth) {
+ uint8_t depth,
+ uint64_t stack_id,
+ uint64_t parent_stack_id) {
start_ns_.emplace_back(start_ns);
durations_.emplace_back(duration_ns);
utids_.emplace_back(utid);
cats_.emplace_back(cat);
names_.emplace_back(name);
depths_.emplace_back(depth);
+ stack_ids_.emplace_back(stack_id);
+ parent_stack_ids_.emplace_back(parent_stack_id);
}
size_t slice_count() const { return start_ns_.size(); }
@@ -122,6 +129,10 @@
const std::deque<StringId>& cats() const { return cats_; }
const std::deque<StringId>& names() const { return names_; }
const std::deque<uint8_t>& depths() const { return depths_; }
+ const std::deque<uint64_t>& stack_ids() const { return stack_ids_; }
+ const std::deque<uint64_t>& parent_stack_ids() const {
+ return parent_stack_ids_;
+ }
private:
std::deque<uint64_t> start_ns_;
@@ -130,6 +141,8 @@
std::deque<StringId> cats_;
std::deque<StringId> names_;
std::deque<uint8_t> depths_;
+ std::deque<uint64_t> stack_ids_;
+ std::deque<uint64_t> parent_stack_ids_;
};
void ResetStorage();
@@ -139,13 +152,13 @@
uint64_t duration_ns,
UniqueTid utid);
- UniqueTid AddEmptyThread() {
- unique_threads_.emplace_back();
+ UniqueTid AddEmptyThread(uint32_t tid) {
+ unique_threads_.emplace_back(tid);
return static_cast<UniqueTid>(unique_threads_.size() - 1);
}
- UniquePid AddEmptyProcess() {
- unique_processes_.emplace_back();
+ UniquePid AddEmptyProcess(uint32_t pid) {
+ unique_processes_.emplace_back(pid);
return static_cast<UniquePid>(unique_processes_.size() - 1);
}
@@ -156,12 +169,12 @@
StringId InternString(const char* data, size_t length);
Process* GetMutableProcess(UniquePid upid) {
- PERFETTO_DCHECK(upid < unique_processes_.size());
+ PERFETTO_DCHECK(upid > 0 && upid < unique_processes_.size());
return &unique_processes_[upid];
}
Thread* GetMutableThread(UniqueTid utid) {
- PERFETTO_DCHECK(utid < unique_threads_.size());
+ PERFETTO_DCHECK(utid > 0 && utid < unique_threads_.size());
return &unique_threads_[utid];
}
@@ -177,12 +190,12 @@
}
const Process& GetProcess(UniquePid upid) const {
- PERFETTO_DCHECK(upid < unique_processes_.size());
+ PERFETTO_DCHECK(upid > 0 && upid < unique_processes_.size());
return unique_processes_[upid];
}
const Thread& GetThread(UniqueTid utid) const {
- PERFETTO_DCHECK(utid < unique_threads_.size());
+ PERFETTO_DCHECK(utid > 0 && utid < unique_threads_.size());
return unique_threads_[utid];
}