Merge "Update to use new UUID format"
diff --git a/Android.bp b/Android.bp
index 514776e..44db57c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4682,6 +4682,7 @@
name: "perfetto_src_trace_processor_unittests",
srcs: [
"src/trace_processor/args_table_unittest.cc",
+ "src/trace_processor/basic_types_unittest.cc",
"src/trace_processor/clock_tracker_unittest.cc",
"src/trace_processor/event_tracker_unittest.cc",
"src/trace_processor/filtered_row_index_unittest.cc",
diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h
index 0b2efbf..0f08b42 100644
--- a/include/perfetto/base/compiler.h
+++ b/include/perfetto/base/compiler.h
@@ -38,25 +38,6 @@
#define PERFETTO_NO_INLINE
#endif
-// TODO(lalitm): is_trivially_constructible is currently not available
-// in some environments we build in. Reenable when that environment supports
-// this.
-#if defined(__GLIBCXX__)
-#define PERFETTO_IS_TRIVIALLY_CONSTRUCTIBLE(T) true
-#else
-#define PERFETTO_IS_TRIVIALLY_CONSTRUCTIBLE(T) \
- std::is_trivially_constructible<T>::value
-#endif
-
-// TODO(lalitm): is_trivially_copyable is currently not available
-// in some environments we build in. Reenable when that environment supports
-// this.
-#if defined(__GLIBCXX__)
-#define PERFETTO_IS_TRIVIALLY_COPYABLE(T) true
-#else
-#define PERFETTO_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
-#endif
-
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() __PRETTY_FUNCTION__
#elif defined(_MSC_VER)
diff --git a/include/perfetto/protozero/proto_decoder.h b/include/perfetto/protozero/proto_decoder.h
index 5794cc9..b58e64e 100644
--- a/include/perfetto/protozero/proto_decoder.h
+++ b/include/perfetto/protozero/proto_decoder.h
@@ -332,7 +332,7 @@
// implicit initializers on all the ~1000 entries. We need it to initialize
// only on the first |max_field_id| fields, the remaining capacity doesn't
// require initialization.
- static_assert(PERFETTO_IS_TRIVIALLY_CONSTRUCTIBLE(Field) &&
+ static_assert(std::is_trivially_constructible<Field>::value &&
std::is_trivially_destructible<Field>::value &&
std::is_trivial<Field>::value,
"Field must be a trivial aggregate type");
diff --git a/include/perfetto/trace_processor/basic_types.h b/include/perfetto/trace_processor/basic_types.h
index efac31c..ca540d2 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -57,6 +57,13 @@
return value;
}
+ static SqlValue Double(double v) {
+ SqlValue value;
+ value.double_value = v;
+ value.type = Type::kDouble;
+ return value;
+ }
+
static SqlValue String(const char* v) {
SqlValue value;
value.string_value = v;
@@ -69,7 +76,7 @@
return double_value;
}
- int Compare(const SqlValue& value) const {
+ int64_t Compare(const SqlValue& value) const {
// TODO(lalitm): this is almost the same as what SQLite does with the
// exception of comparisions between long and double - we choose (for
// performance reasons) to omit comparisions between them.
@@ -80,10 +87,11 @@
case Type::kNull:
return 0;
case Type::kLong:
- return static_cast<int>(long_value - value.long_value);
+ return long_value - value.long_value;
case Type::kDouble: {
- double diff = double_value - value.double_value;
- return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
+ return double_value < value.double_value
+ ? -1
+ : (double_value > value.double_value ? 1 : 0);
}
case Type::kString:
return strcmp(string_value, value.string_value);
@@ -92,7 +100,7 @@
int ret = memcmp(bytes_value, value.bytes_value, bytes);
if (ret != 0)
return ret;
- return static_cast<int>(bytes_count - value.bytes_count);
+ return static_cast<int64_t>(bytes_count - value.bytes_count);
}
}
PERFETTO_FATAL("For GCC");
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index d126980..d8352b4 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -3049,7 +3049,7 @@
ROOT_JNI_MONITOR = 14;
};
// Objects retained by this root.
- repeated uint64 object_ids = 1;
+ repeated uint64 object_ids = 1 [packed = true];
optional Type root_type = 2;
}
@@ -3065,10 +3065,10 @@
// Indices for InternedData.field_names for the name of the field referring
// to the object.
- repeated uint64 reference_field_id = 4;
+ repeated uint64 reference_field_id = 4 [packed = true];
// Ids of the Object that is referred to.
- repeated uint64 reference_object_id = 5;
+ repeated uint64 reference_object_id = 5 [packed = true];
}
message HeapGraph {
diff --git a/protos/perfetto/trace/profiling/heap_graph.proto b/protos/perfetto/trace/profiling/heap_graph.proto
index d29c292..7e2d5d4 100644
--- a/protos/perfetto/trace/profiling/heap_graph.proto
+++ b/protos/perfetto/trace/profiling/heap_graph.proto
@@ -43,7 +43,7 @@
ROOT_JNI_MONITOR = 14;
};
// Objects retained by this root.
- repeated uint64 object_ids = 1;
+ repeated uint64 object_ids = 1 [packed = true];
optional Type root_type = 2;
}
@@ -59,10 +59,10 @@
// Indices for InternedData.field_names for the name of the field referring
// to the object.
- repeated uint64 reference_field_id = 4;
+ repeated uint64 reference_field_id = 4 [packed = true];
// Ids of the Object that is referred to.
- repeated uint64 reference_object_id = 5;
+ repeated uint64 reference_object_id = 5 [packed = true];
}
message HeapGraph {
diff --git a/src/protozero/proto_decoder.cc b/src/protozero/proto_decoder.cc
index bd563fb..a5f1b95 100644
--- a/src/protozero/proto_decoder.cc
+++ b/src/protozero/proto_decoder.cc
@@ -208,7 +208,7 @@
PERFETTO_CHECK(new_capacity > size_);
std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
- static_assert(PERFETTO_IS_TRIVIALLY_COPYABLE(Field),
+ static_assert(std::is_trivially_copyable<Field>::value,
"Field must be trivially copyable");
memcpy(&new_storage[0], fields_, sizeof(Field) * size_);
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 80d394d..a096cd2 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -357,6 +357,7 @@
perfetto_unittest_source_set("unittests") {
testonly = true
sources = [
+ "basic_types_unittest.cc",
"clock_tracker_unittest.cc",
"event_tracker_unittest.cc",
"forwarding_trace_parser_unittest.cc",
diff --git a/src/trace_processor/basic_types_unittest.cc b/src/trace_processor/basic_types_unittest.cc
new file mode 100644
index 0000000..a2a0fd2
--- /dev/null
+++ b/src/trace_processor/basic_types_unittest.cc
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(SqlValueTest, DifferentTypes) {
+ ASSERT_LT(SqlValue(), SqlValue::Long(10));
+ ASSERT_LT(SqlValue::Long(10), SqlValue::Double(10.0));
+ ASSERT_LT(SqlValue::Double(10.0), SqlValue::String("10"));
+}
+
+TEST(SqlValueTest, CompareLong) {
+ SqlValue int32_min = SqlValue::Long(std::numeric_limits<int32_t>::min());
+ SqlValue minus_1 = SqlValue::Long(-1);
+ SqlValue zero = SqlValue::Long(0);
+ SqlValue uint32_max = SqlValue::Long(std::numeric_limits<uint32_t>::max());
+
+ ASSERT_LT(int32_min, minus_1);
+ ASSERT_LT(int32_min, uint32_max);
+ ASSERT_LT(minus_1, uint32_max);
+
+ ASSERT_GT(uint32_max, zero);
+
+ ASSERT_EQ(zero, zero);
+}
+
+TEST(SqlValueTest, CompareDouble) {
+ SqlValue int32_min = SqlValue::Double(std::numeric_limits<int32_t>::min());
+ SqlValue minus_1 = SqlValue::Double(-1.0);
+ SqlValue zero = SqlValue::Double(0);
+ SqlValue uint32_max = SqlValue::Double(std::numeric_limits<uint32_t>::max());
+
+ ASSERT_LT(int32_min, minus_1);
+ ASSERT_LT(int32_min, uint32_max);
+ ASSERT_LT(minus_1, uint32_max);
+
+ ASSERT_GT(uint32_max, zero);
+
+ ASSERT_EQ(zero, zero);
+}
+
+TEST(SqlValueTest, CompareString) {
+ SqlValue a = SqlValue::String("a");
+ SqlValue aa = SqlValue::String("aa");
+ SqlValue b = SqlValue::String("b");
+
+ ASSERT_LT(a, aa);
+ ASSERT_LT(aa, b);
+ ASSERT_LT(a, b);
+
+ ASSERT_GT(aa, a);
+
+ ASSERT_EQ(a, a);
+ ASSERT_EQ(aa, SqlValue::String("aa"));
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/bit_vector_benchmark.cc b/src/trace_processor/db/bit_vector_benchmark.cc
index 6c896d1..54c00f9 100644
--- a/src/trace_processor/db/bit_vector_benchmark.cc
+++ b/src/trace_processor/db/bit_vector_benchmark.cc
@@ -22,6 +22,20 @@
using perfetto::trace_processor::BitVector;
+bool IsBenchmarkFunctionalOnly() {
+ return getenv("BENCHMARK_FUNCTIONAL_TEST_ONLY") != nullptr;
+}
+
+void BitVectorArgs(benchmark::internal::Benchmark* b) {
+ b->Arg(64);
+
+ if (!IsBenchmarkFunctionalOnly()) {
+ b->Arg(512);
+ b->Arg(8192);
+ b->Arg(123456);
+ b->Arg(1234567);
+ }
+}
}
static void BM_BitVectorAppendTrue(benchmark::State& state) {
@@ -72,12 +86,7 @@
benchmark::ClobberMemory();
}
}
-BENCHMARK(BM_BitVectorSet)
- ->Arg(64)
- ->Arg(512)
- ->Arg(8192)
- ->Arg(123456)
- ->Arg(1234567);
+BENCHMARK(BM_BitVectorSet)->Apply(BitVectorArgs);
static void BM_BitVectorClear(benchmark::State& state) {
static constexpr uint32_t kRandomSeed = 42;
@@ -107,12 +116,7 @@
benchmark::ClobberMemory();
}
}
-BENCHMARK(BM_BitVectorClear)
- ->Arg(64)
- ->Arg(512)
- ->Arg(8192)
- ->Arg(123456)
- ->Arg(1234567);
+BENCHMARK(BM_BitVectorClear)->Apply(BitVectorArgs);
static void BM_BitVectorIndexOfNthSet(benchmark::State& state) {
static constexpr uint32_t kRandomSeed = 42;
@@ -142,12 +146,7 @@
pool_idx = (pool_idx + 1) % kPoolSize;
}
}
-BENCHMARK(BM_BitVectorIndexOfNthSet)
- ->Arg(64)
- ->Arg(512)
- ->Arg(8192)
- ->Arg(123456)
- ->Arg(1234567);
+BENCHMARK(BM_BitVectorIndexOfNthSet)->Apply(BitVectorArgs);
static void BM_BitVectorGetNumBitsSet(benchmark::State& state) {
static constexpr uint32_t kRandomSeed = 42;
@@ -175,12 +174,7 @@
}
PERFETTO_CHECK(res == count);
}
-BENCHMARK(BM_BitVectorGetNumBitsSet)
- ->Arg(64)
- ->Arg(512)
- ->Arg(8192)
- ->Arg(123456)
- ->Arg(1234567);
+BENCHMARK(BM_BitVectorGetNumBitsSet)->Apply(BitVectorArgs);
static void BM_BitVectorResize(benchmark::State& state) {
static constexpr uint32_t kRandomSeed = 42;
@@ -239,9 +233,4 @@
benchmark::ClobberMemory();
}
}
-BENCHMARK(BM_BitVectorUpdateSetBits)
- ->Arg(64)
- ->Arg(512)
- ->Arg(8192)
- ->Arg(123456)
- ->Arg(1234567);
+BENCHMARK(BM_BitVectorUpdateSetBits)->Apply(BitVectorArgs);
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index b2af643..518ac6a 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -142,6 +142,16 @@
PERFETTO_FATAL("For GCC");
}
+ // Sorts |idx| in ascending or descending order (determined by |desc|) based
+ // on the contents of this column.
+ void StableSort(bool desc, std::vector<uint32_t>* idx) const {
+ if (desc) {
+ StableSort<true /* desc */>(idx);
+ } else {
+ StableSort<false /* desc */>(idx);
+ }
+ }
+
// Updates the given RowMap by only keeping rows where this column meets the
// given filter constraint.
void FilterInto(FilterOp op, SqlValue value, RowMap* rm) const {
@@ -580,6 +590,63 @@
}
}
+ template <bool desc>
+ void StableSort(std::vector<uint32_t>* out) const {
+ switch (type_) {
+ case ColumnType::kInt32: {
+ if (IsNullable()) {
+ StableSort<desc, int32_t, true /* is_nullable */>(out);
+ } else {
+ StableSort<desc, int32_t, false /* is_nullable */>(out);
+ }
+ break;
+ }
+ case ColumnType::kUint32: {
+ if (IsNullable()) {
+ StableSort<desc, uint32_t, true /* is_nullable */>(out);
+ } else {
+ StableSort<desc, uint32_t, false /* is_nullable */>(out);
+ }
+ break;
+ }
+ case ColumnType::kInt64: {
+ if (IsNullable()) {
+ StableSort<desc, int64_t, true /* is_nullable */>(out);
+ } else {
+ StableSort<desc, int64_t, false /* is_nullable */>(out);
+ }
+ break;
+ }
+ case ColumnType::kString: {
+ row_map().StableSort(out, [this](uint32_t a_idx, uint32_t b_idx) {
+ auto a_str = GetStringPoolStringAtIdx(a_idx);
+ auto b_str = GetStringPoolStringAtIdx(b_idx);
+ return desc ? b_str < a_str : a_str < b_str;
+ });
+ break;
+ }
+ case ColumnType::kId:
+ row_map().StableSort(out, [](uint32_t a_idx, uint32_t b_idx) {
+ return desc ? b_idx < a_idx : a_idx < b_idx;
+ });
+ }
+ }
+
+ template <bool desc, typename T, bool is_nullable>
+ void StableSort(std::vector<uint32_t>* out) const {
+ const auto& sv = sparse_vector<T>();
+ row_map().StableSort(out, [&sv](uint32_t a_idx, uint32_t b_idx) {
+ if (is_nullable) {
+ auto a_val = sv.Get(a_idx);
+ auto b_val = sv.Get(b_idx);
+ return desc ? b_val < a_val : a_val < b_val;
+ }
+ auto a_val = sv.GetNonNull(a_idx);
+ auto b_val = sv.GetNonNull(b_idx);
+ return desc ? b_val < a_val : a_val < b_val;
+ });
+ }
+
template <typename T>
static ColumnType ToColumnType() {
if (std::is_same<T, uint32_t>::value) {
diff --git a/src/trace_processor/db/row_map.h b/src/trace_processor/db/row_map.h
index 53264da..543d2c2 100644
--- a/src/trace_processor/db/row_map.h
+++ b/src/trace_processor/db/row_map.h
@@ -127,6 +127,9 @@
}
}
+ Iterator(Iterator&&) noexcept = default;
+ Iterator& operator=(Iterator&&) = default;
+
// Forwards the iterator to the next row of the RowMap.
void Next() {
switch (rm_->mode_) {
@@ -190,6 +193,9 @@
}
private:
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+
// Only one of the below will be non-null depending on the mode of the
// RowMap.
std::unique_ptr<RangeIterator> range_it_;
@@ -434,6 +440,26 @@
}
}
+ template <typename Comparator>
+ void StableSort(std::vector<uint32_t>* out, Comparator c) const {
+ switch (mode_) {
+ case Mode::kRange: {
+ StableSort(out, c, [this](uint32_t off) { return start_idx_ + off; });
+ break;
+ }
+ case Mode::kBitVector: {
+ StableSort(out, c, [this](uint32_t off) {
+ return bit_vector_.IndexOfNthSet(off);
+ });
+ break;
+ }
+ case Mode::kIndexVector: {
+ StableSort(out, c, [this](uint32_t off) { return index_vector_[off]; });
+ break;
+ }
+ }
+ }
+
// Returns the iterator over the rows in this RowMap.
Iterator IterateRows() const { return Iterator(this); }
@@ -530,6 +556,13 @@
}
}
+ template <typename Comparator, typename Indexer>
+ void StableSort(std::vector<uint32_t>* out, Comparator c, Indexer i) const {
+ std::stable_sort(
+ out->begin(), out->end(),
+ [&c, &i](uint32_t a, uint32_t b) { return c(i(a), i(b)); });
+ }
+
RowMap SelectRowsSlow(const RowMap& selector) const;
Mode mode_ = Mode::kRange;
diff --git a/src/trace_processor/db/table.cc b/src/trace_processor/db/table.cc
index 3b22356..d23ba87 100644
--- a/src/trace_processor/db/table.cc
+++ b/src/trace_processor/db/table.cc
@@ -96,17 +96,36 @@
std::vector<uint32_t> idx(size_);
std::iota(idx.begin(), idx.end(), 0);
- // Sort the row indices according to the given order by constraints.
- std::sort(idx.begin(), idx.end(), [this, &od](uint32_t a, uint32_t b) {
- for (const Order& o : od) {
- const Column& col = columns_[o.col_idx];
- int cmp =
- col.Get(a) < col.Get(b) ? -1 : (col.Get(b) < col.Get(a) ? 1 : 0);
- if (cmp != 0)
- return o.desc ? cmp > 0 : cmp < 0;
- }
- return false;
- });
+ // As our data is columnar, it's always more efficient to sort one column
+ // at a time rather than try and sort lexiographically all at once.
+ // To preserve correctness, we need to stably sort the index vector once
+ // for each order by in *reverse* order. Reverse order is important as it
+ // preserves the lexiographical property.
+ //
+ // For example, suppose we have the following:
+ // Table {
+ // Column x;
+ // Column y
+ // Column z;
+ // }
+ //
+ // Then, to sort "y asc, x desc", we could do one of two things:
+ // 1) sort the index vector all at once and on each index, we compare
+ // y then z. This is slow as the data is columnar and we need to
+ // repeatedly branch inside each column.
+ // 2) we can stably sort first on x desc and then sort on y asc. This will
+ // first put all the x in the correct order such that when we sort on
+ // y asc, we will have the correct order of x where y is the same (since
+ // the sort is stable).
+ //
+ // TODO(lalitm): it is possible that we could sort the last constraint (i.e.
+ // the first constraint in the below loop) in a non-stable way. However, this
+ // is more subtle than it appears as we would then need special handling where
+ // there are order bys on a column which is already sorted (e.g. ts, id).
+ // Investigate whether the performance gains from this are worthwhile.
+ for (auto it = od.rbegin(); it != od.rend(); ++it) {
+ columns_[it->col_idx].StableSort(it->desc, &idx);
+ }
// Return a copy of this table with the RowMaps using the computed ordered
// RowMap.
diff --git a/src/trace_processor/db/table.h b/src/trace_processor/db/table.h
index e9410b4..a5e71b9 100644
--- a/src/trace_processor/db/table.h
+++ b/src/trace_processor/db/table.h
@@ -43,6 +43,9 @@
}
}
+ Iterator(Iterator&&) noexcept = default;
+ Iterator& operator=(Iterator&&) = default;
+
// Advances the iterator to the next row of the table.
void Next() {
for (auto& it : its_) {
@@ -60,6 +63,9 @@
}
private:
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+
const Table* table_ = nullptr;
std::vector<RowMap::Iterator> its_;
};
diff --git a/src/trace_processor/importers/proto/heap_graph_module.cc b/src/trace_processor/importers/proto/heap_graph_module.cc
index 50bba45..7216d90 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.cc
+++ b/src/trace_processor/importers/proto/heap_graph_module.cc
@@ -65,6 +65,28 @@
}
}
+// Iterate over a repeated field of varints, independent of whether it is
+// packed or not.
+template <int32_t field_no, typename T, typename F>
+bool ForEachVarInt(const T& decoder, F fn) {
+ auto field = decoder.template at<field_no>();
+ bool parse_error = false;
+ if (field.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+ // packed repeated
+ auto it = decoder.template GetPackedRepeated<
+ ::protozero::proto_utils::ProtoWireType::kVarInt, uint64_t>(
+ field_no, &parse_error);
+ for (; it; ++it)
+ fn(*it);
+ } else {
+ // non-packed repeated
+ auto it = decoder.template GetRepeated<uint64_t>(field_no);
+ for (; it; ++it)
+ fn(*it);
+ }
+ return parse_error;
+}
+
} // namespace
void HeapGraphModule::ParseHeapGraph(int64_t ts, protozero::ConstBytes blob) {
@@ -78,21 +100,37 @@
obj.object_id = object.id();
obj.self_size = object.self_size();
obj.type_id = object.type_id();
- auto ref_field_ids_it = object.reference_field_id();
- auto ref_object_ids_it = object.reference_object_id();
- for (; ref_field_ids_it && ref_object_ids_it;
- ++ref_field_ids_it, ++ref_object_ids_it) {
- HeapGraphTracker::SourceObject::Reference ref;
- ref.field_name_id = *ref_field_ids_it;
- ref.owned_object_id = *ref_object_ids_it;
- obj.references.emplace_back(std::move(ref));
+
+ std::vector<uint64_t> field_ids;
+ std::vector<uint64_t> object_ids;
+
+ bool parse_error = ForEachVarInt<
+ protos::pbzero::HeapGraphObject::kReferenceFieldIdFieldNumber>(
+ object, [&field_ids](uint64_t value) { field_ids.push_back(value); });
+
+ if (!parse_error) {
+ parse_error = ForEachVarInt<
+ protos::pbzero::HeapGraphObject::kReferenceObjectIdFieldNumber>(
+ object,
+ [&object_ids](uint64_t value) { object_ids.push_back(value); });
}
- if (ref_field_ids_it || ref_object_ids_it) {
- context_->storage->IncrementIndexedStats(stats::heap_graph_missing_packet,
- static_cast<int>(upid));
+ if (parse_error) {
+ context_->storage->IncrementIndexedStats(
+ stats::heap_graph_malformed_packet, static_cast<int>(upid));
+ break;
+ }
+ if (field_ids.size() != object_ids.size()) {
+ context_->storage->IncrementIndexedStats(
+ stats::heap_graph_malformed_packet, static_cast<int>(upid));
continue;
}
+ for (size_t i = 0; i < field_ids.size(); ++i) {
+ HeapGraphTracker::SourceObject::Reference ref;
+ ref.field_name_id = field_ids[i];
+ ref.owned_object_id = object_ids[i];
+ obj.references.emplace_back(std::move(ref));
+ }
context_->heap_graph_tracker->AddObject(upid, ts, std::move(obj));
}
for (auto it = heap_graph.type_names(); it; ++it) {
@@ -118,8 +156,16 @@
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);
+ bool parse_error =
+ ForEachVarInt<protos::pbzero::HeapGraphRoot::kObjectIdsFieldNumber>(
+ entry, [&src_root](uint64_t value) {
+ src_root.object_ids.emplace_back(value);
+ });
+ if (parse_error) {
+ context_->storage->IncrementIndexedStats(
+ stats::heap_graph_malformed_packet, static_cast<int>(upid));
+ break;
+ }
context_->heap_graph_tracker->AddRoot(upid, ts, std::move(src_root));
}
if (!heap_graph.continued()) {
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.cc b/src/trace_processor/importers/proto/heap_graph_tracker.cc
index e2d6aef..55af73d 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.cc
@@ -95,6 +95,7 @@
int64_t reference_set_id =
context_->storage->heap_graph_reference_table().size();
+ std::set<int64_t> seen_owned;
for (const SourceObject::Reference& ref : obj.references) {
// This is true for unset reference fields.
if (ref.owned_object_id == 0)
@@ -107,7 +108,10 @@
continue;
int64_t owned_row = it->second;
- walker_.AddEdge(owner_row, owned_row);
+ bool inserted;
+ std::tie(std::ignore, inserted) = seen_owned.emplace(owned_row);
+ if (inserted)
+ walker_.AddEdge(owner_row, owned_row);
auto field_name_it = interned_field_names_.find(ref.field_name_id);
if (field_name_it == interned_field_names_.end()) {
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.cc b/src/trace_processor/importers/proto/heap_graph_walker.cc
index 8655d85..b512571 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_walker.cc
@@ -66,8 +66,11 @@
}
void HeapGraphWalker::AddEdge(int64_t owner_row, int64_t owned_row) {
- GetNode(owner_row).children.emplace(&GetNode(owned_row));
- GetNode(owned_row).parents.emplace(&GetNode(owner_row));
+ Node& owner_node = GetNode(owner_row);
+ Node& owned_node = GetNode(owned_row);
+
+ owner_node.children.emplace_back(&owned_node);
+ owned_node.parents.emplace_back(&owner_node);
}
void HeapGraphWalker::MarkRoot(int64_t row) {
@@ -268,23 +271,43 @@
}
void HeapGraphWalker::FindSCC(Node* node) {
- node->node_index = node->lowlink = next_node_index_++;
- node_stack_.push_back(node);
- node->on_stack = true;
- for (Node* child : node->children) {
- PERFETTO_CHECK(child->reachable);
- if (child->node_index == 0) {
- FindSCC(child);
- if (child->lowlink < node->lowlink)
- node->lowlink = child->lowlink;
- } else if (child->on_stack) {
- if (child->node_index < node->lowlink)
+ std::vector<Node*> walk_stack;
+ std::vector<size_t> walk_child;
+
+ walk_stack.emplace_back(node);
+ walk_child.emplace_back(0);
+
+ while (!walk_stack.empty()) {
+ node = walk_stack.back();
+ size_t& child_idx = walk_child.back();
+
+ if (child_idx == 0) {
+ node->node_index = node->lowlink = next_node_index_++;
+ node_stack_.push_back(node);
+ node->on_stack = true;
+ } else {
+ Node* prev_child = node->children[child_idx - 1];
+ if (prev_child->node_index > node->node_index &&
+ prev_child->lowlink < node->lowlink)
+ node->lowlink = prev_child->lowlink;
+ }
+
+ if (child_idx == node->children.size()) {
+ if (node->lowlink == node->node_index)
+ FoundSCC(node);
+ walk_stack.pop_back();
+ walk_child.pop_back();
+ } else {
+ Node* child = node->children[child_idx++];
+ PERFETTO_CHECK(child->reachable);
+ if (child->node_index == 0) {
+ walk_stack.emplace_back(child);
+ walk_child.emplace_back(0);
+ } else if (child->on_stack && child->node_index < node->lowlink) {
node->lowlink = child->node_index;
+ }
}
}
-
- if (node->lowlink == node->node_index)
- FoundSCC(node);
}
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/heap_graph_walker.h b/src/trace_processor/importers/proto/heap_graph_walker.h
index b23f2a3..c05c6df 100644
--- a/src/trace_processor/importers/proto/heap_graph_walker.h
+++ b/src/trace_processor/importers/proto/heap_graph_walker.h
@@ -120,11 +120,8 @@
private:
struct Node {
- // These are sets to conveniently get rid of double edges between nodes.
- // We do not care if an object owns another object via multiple references
- // or only one.
- std::set<Node*> children;
- std::set<Node*> parents;
+ std::vector<Node*> children;
+ std::vector<Node*> parents;
uint64_t self_size = 0;
uint64_t retained_size = 0;
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 14afab1..e93578c 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -212,7 +212,12 @@
} else {
auto args = proc.cmdline();
base::StringView argv0 = args ? *args : base::StringView();
- context_->process_tracker->SetProcessMetadata(pid, ppid, argv0);
+ UniquePid upid =
+ context_->process_tracker->SetProcessMetadata(pid, ppid, argv0);
+ if (proc.has_uid()) {
+ context_->process_tracker->SetProcessUid(
+ upid, static_cast<uint32_t>(proc.uid()));
+ }
}
}
diff --git a/src/trace_processor/process_table.cc b/src/trace_processor/process_table.cc
index adc9d1e..65d29f9 100644
--- a/src/trace_processor/process_table.cc
+++ b/src/trace_processor/process_table.cc
@@ -47,6 +47,7 @@
SqliteTable::Column(Column::kEndTs, "end_ts", SqlValue::Type::kLong),
SqliteTable::Column(Column::kParentUpid, "parent_upid",
SqlValue::Type::kLong),
+ SqliteTable::Column(Column::kUid, "uid", SqlValue::Type::kLong),
},
{Column::kUpid});
return util::OkStatus();
@@ -147,6 +148,14 @@
}
break;
}
+ case Column::kUid: {
+ if (process.uid.has_value()) {
+ sqlite3_result_int64(context, process.uid.value());
+ } else {
+ sqlite3_result_null(context);
+ }
+ 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 c4c1ed5..74b0386 100644
--- a/src/trace_processor/process_table.h
+++ b/src/trace_processor/process_table.h
@@ -36,7 +36,8 @@
kPid = 2,
kStartTs = 3,
kEndTs = 4,
- kParentUpid = 5
+ kParentUpid = 5,
+ kUid = 6
};
class Cursor : public SqliteTable::Cursor {
public:
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc
index e9c0335..1ccc4b7 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/process_tracker.cc
@@ -194,6 +194,10 @@
return upid;
}
+void ProcessTracker::SetProcessUid(UniquePid upid, uint32_t uid) {
+ context_->storage->GetMutableProcess(upid)->uid = uid;
+}
+
void ProcessTracker::SetProcessNameIfUnset(UniquePid upid,
StringId process_name_id) {
TraceStorage::Process* process = context_->storage->GetMutableProcess(upid);
diff --git a/src/trace_processor/process_tracker.h b/src/trace_processor/process_tracker.h
index 7c7ea55..985212b 100644
--- a/src/trace_processor/process_tracker.h
+++ b/src/trace_processor/process_tracker.h
@@ -91,6 +91,9 @@
base::Optional<uint32_t> ppid,
base::StringView name);
+ // Sets the process user id.
+ void SetProcessUid(UniquePid upid, uint32_t uid);
+
// Assigns the given name to the process identified by |upid| if it does not
// have a name yet.
void SetProcessNameIfUnset(UniquePid upid, StringId process_name_id);
diff --git a/src/trace_processor/span_join_operator_table.cc b/src/trace_processor/span_join_operator_table.cc
index 931289c..24077d7 100644
--- a/src/trace_processor/span_join_operator_table.cc
+++ b/src/trace_processor/span_join_operator_table.cc
@@ -169,8 +169,14 @@
return std::unique_ptr<SpanJoinOperatorTable::Cursor>(new Cursor(this, db_));
}
-int SpanJoinOperatorTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
+int SpanJoinOperatorTable::BestIndex(const QueryConstraints& qc,
+ BestIndexInfo* info) {
// TODO(lalitm): figure out cost estimation.
+ const auto& ob = qc.order_by();
+ if (ob.size() == 1 && ob.front().iColumn == Column::kTimestamp &&
+ !ob.front().desc) {
+ info->sqlite_omit_order_by = true;
+ }
return SQLITE_OK;
}
@@ -187,8 +193,7 @@
continue;
if (col_name == kTsColumnName || col_name == kDurColumnName) {
- // We don't support constraints on ts or duration in the child tables.
- PERFETTO_DFATAL("ts or duration constraints on child tables");
+ // Allow SQLite handle any constraints on ts or duration.
continue;
}
auto op = sqlite_utils::OpToString(cs.op);
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 6afcbfe..d8da768 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -30,6 +30,9 @@
public:
explicit Cursor(DbSqliteTable* table);
+ Cursor(Cursor&&) noexcept = default;
+ Cursor& operator=(Cursor&&) = default;
+
// Implementation of SqliteTable::Cursor.
int Filter(const QueryConstraints& qc, sqlite3_value** argv) override;
int Next() override;
@@ -37,6 +40,9 @@
int Column(sqlite3_context*, int N) override;
private:
+ Cursor(const Cursor&) = delete;
+ Cursor& operator=(const Cursor&) = delete;
+
const Table* initial_db_table_ = nullptr;
base::Optional<Table> db_table_;
diff --git a/src/trace_processor/tables/macros_benchmark.cc b/src/trace_processor/tables/macros_benchmark.cc
index 779acb2..8150371 100644
--- a/src/trace_processor/tables/macros_benchmark.cc
+++ b/src/trace_processor/tables/macros_benchmark.cc
@@ -44,6 +44,32 @@
} // namespace trace_processor
} // namespace perfetto
+namespace {
+
+bool IsBenchmarkFunctionalOnly() {
+ return getenv("BENCHMARK_FUNCTIONAL_TEST_ONLY") != nullptr;
+}
+
+void TableFilterArgs(benchmark::internal::Benchmark* b) {
+ if (IsBenchmarkFunctionalOnly()) {
+ b->Arg(1024);
+ } else {
+ b->RangeMultiplier(8);
+ b->Range(1024, 2 * 1024 * 1024);
+ }
+}
+
+void TableSortArgs(benchmark::internal::Benchmark* b) {
+ if (IsBenchmarkFunctionalOnly()) {
+ b->Arg(64);
+ } else {
+ b->RangeMultiplier(8);
+ b->Range(1024, 256 * 1024);
+ }
+}
+
+} // namespace
+
using perfetto::trace_processor::ChildTestTable;
using perfetto::trace_processor::RootTestTable;
using perfetto::trace_processor::SqlValue;
@@ -80,9 +106,7 @@
it = child.IterateRows();
}
}
-BENCHMARK(BM_TableIteratorChild)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableIteratorChild)->Apply(TableFilterArgs);
static void BM_TableFilterIdColumn(benchmark::State& state) {
StringPool pool;
@@ -96,9 +120,7 @@
benchmark::DoNotOptimize(root.Filter({root.id().eq(SqlValue::Long(30))}));
}
}
-BENCHMARK(BM_TableFilterIdColumn)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterIdColumn)->Apply(TableFilterArgs);
static void BM_TableFilterRootNonNullEqMatchMany(benchmark::State& state) {
StringPool pool;
@@ -118,9 +140,7 @@
root.Filter({root.root_non_null().eq(SqlValue::Long(0))}));
}
}
-BENCHMARK(BM_TableFilterRootNonNullEqMatchMany)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterRootNonNullEqMatchMany)->Apply(TableFilterArgs);
static void BM_TableFilterRootNullableEqMatchMany(benchmark::State& state) {
StringPool pool;
@@ -144,9 +164,7 @@
root.Filter({root.root_nullable().eq(SqlValue::Long(1))}));
}
}
-BENCHMARK(BM_TableFilterRootNullableEqMatchMany)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterRootNullableEqMatchMany)->Apply(TableFilterArgs);
static void BM_TableFilterChildNonNullEqMatchMany(benchmark::State& state) {
StringPool pool;
@@ -169,9 +187,7 @@
child.Filter({child.child_non_null().eq(SqlValue::Long(0))}));
}
}
-BENCHMARK(BM_TableFilterChildNonNullEqMatchMany)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterChildNonNullEqMatchMany)->Apply(TableFilterArgs);
static void BM_TableFilterChildNullableEqMatchMany(benchmark::State& state) {
StringPool pool;
@@ -197,9 +213,7 @@
child.Filter({child.child_nullable().eq(SqlValue::Long(1))}));
}
}
-BENCHMARK(BM_TableFilterChildNullableEqMatchMany)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterChildNullableEqMatchMany)->Apply(TableFilterArgs);
static void BM_TableFilterChildNonNullEqMatchManyInParent(
benchmark::State& state) {
@@ -224,8 +238,7 @@
}
}
BENCHMARK(BM_TableFilterChildNonNullEqMatchManyInParent)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+ ->Apply(TableFilterArgs);
static void BM_TableFilterChildNullableEqMatchManyInParent(
benchmark::State& state) {
@@ -250,8 +263,7 @@
}
}
BENCHMARK(BM_TableFilterChildNullableEqMatchManyInParent)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+ ->Apply(TableFilterArgs);
static void BM_TableFilterParentSortedEq(benchmark::State& state) {
StringPool pool;
@@ -270,9 +282,7 @@
root.Filter({root.root_sorted().eq(SqlValue::Long(22))}));
}
}
-BENCHMARK(BM_TableFilterParentSortedEq)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterParentSortedEq)->Apply(TableFilterArgs);
static void BM_TableFilterChildSortedEq(benchmark::State& state) {
StringPool pool;
@@ -293,9 +303,7 @@
child.Filter({child.child_sorted().eq(SqlValue::Long(22))}));
}
}
-BENCHMARK(BM_TableFilterChildSortedEq)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterChildSortedEq)->Apply(TableFilterArgs);
static void BM_TableFilterChildSortedEqInParent(benchmark::State& state) {
StringPool pool;
@@ -319,9 +327,7 @@
child.Filter({child.root_sorted().eq(SqlValue::Long(22))}));
}
}
-BENCHMARK(BM_TableFilterChildSortedEqInParent)
- ->RangeMultiplier(8)
- ->Range(1024, 2 * 1024 * 1024);
+BENCHMARK(BM_TableFilterChildSortedEqInParent)->Apply(TableFilterArgs);
static void BM_TableSortRootNonNull(benchmark::State& state) {
StringPool pool;
@@ -342,7 +348,7 @@
benchmark::DoNotOptimize(root.Sort({root.root_non_null().ascending()}));
}
}
-BENCHMARK(BM_TableSortRootNonNull)->RangeMultiplier(8)->Range(1024, 256 * 1024);
+BENCHMARK(BM_TableSortRootNonNull)->Apply(TableSortArgs);
static void BM_TableSortRootNullable(benchmark::State& state) {
StringPool pool;
@@ -365,9 +371,7 @@
benchmark::DoNotOptimize(root.Sort({root.root_nullable().ascending()}));
}
}
-BENCHMARK(BM_TableSortRootNullable)
- ->RangeMultiplier(8)
- ->Range(1024, 256 * 1024);
+BENCHMARK(BM_TableSortRootNullable)->Apply(TableSortArgs);
static void BM_TableSortChildNonNullInParent(benchmark::State& state) {
StringPool pool;
@@ -395,9 +399,7 @@
benchmark::DoNotOptimize(child.Sort({child.root_non_null().ascending()}));
}
}
-BENCHMARK(BM_TableSortChildNonNullInParent)
- ->RangeMultiplier(8)
- ->Range(1024, 256 * 1024);
+BENCHMARK(BM_TableSortChildNonNullInParent)->Apply(TableSortArgs);
static void BM_TableSortChildNullableInParent(benchmark::State& state) {
StringPool pool;
@@ -429,6 +431,4 @@
benchmark::DoNotOptimize(child.Sort({child.root_nullable().ascending()}));
}
}
-BENCHMARK(BM_TableSortChildNullableInParent)
- ->RangeMultiplier(8)
- ->Range(1024, 256 * 1024);
+BENCHMARK(BM_TableSortChildNullableInParent)->Apply(TableSortArgs);
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 7243b42..2d40985 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -115,6 +115,7 @@
StringId name_id = 0;
uint32_t pid = 0;
base::Optional<UniquePid> parent_upid;
+ base::Optional<uint32_t> uid;
};
// Information about a unique thread seen in a trace.
diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc
index 6c9d2da..de68e0d 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -171,9 +171,14 @@
}
void ProcessStatsDataSource::OnPids(const std::vector<int32_t>& pids) {
- PERFETTO_METATRACE_SCOPED(TAG_PROC_POLLERS, PS_ON_PIDS);
if (!enable_on_demand_dumps_)
return;
+ WriteProcessTree(pids);
+}
+
+void ProcessStatsDataSource::WriteProcessTree(
+ const std::vector<int32_t>& pids) {
+ PERFETTO_METATRACE_SCOPED(TAG_PROC_POLLERS, PS_ON_PIDS);
PERFETTO_DCHECK(!cur_ps_tree_);
int pids_scanned = 0;
for (int32_t pid : pids) {
@@ -414,7 +419,7 @@
// Ensure that we write once long-term process info (e.g., name) for new pids
// that we haven't seen before.
- OnPids(pids);
+ WriteProcessTree(pids);
}
// Returns true if the stats for the given |pid| have been written, false it
diff --git a/src/traced/probes/ps/process_stats_data_source.h b/src/traced/probes/ps/process_stats_data_source.h
index b2fa51c..f301e5d 100644
--- a/src/traced/probes/ps/process_stats_data_source.h
+++ b/src/traced/probes/ps/process_stats_data_source.h
@@ -105,6 +105,9 @@
void WriteAllProcessStats();
bool WriteMemCounters(int32_t pid, const std::string& proc_status);
+ // Scans /proc/pid/status and writes the ProcessTree packet for input pids.
+ void WriteProcessTree(const std::vector<int32_t>& pids);
+
// Read and "latch" the current procfs scan-start timestamp, which
// we reset only in FinalizeCurPacket.
uint64_t CacheProcFsScanStartTimestamp();
diff --git a/test/synth_common.py b/test/synth_common.py
index 3383b81..142e76e 100644
--- a/test/synth_common.py
+++ b/test/synth_common.py
@@ -173,11 +173,13 @@
if ts is not None:
self.packet.timestamp = ts
- def add_process(self, pid, ppid, cmdline):
+ def add_process(self, pid, ppid, cmdline, uid=None):
process = self.packet.process_tree.processes.add()
process.pid = pid
process.ppid = ppid
process.cmdline.append(cmdline)
+ if uid is not None:
+ process.uid = uid
self.proc_map[pid] = cmdline
def add_thread(self, tid, tgid, cmdline):
diff --git a/test/trace_processor/counters_ref_type_null.sql b/test/trace_processor/counters_ref_type_null.sql
index 557b933..36a0b90 100644
--- a/test/trace_processor/counters_ref_type_null.sql
+++ b/test/trace_processor/counters_ref_type_null.sql
@@ -1,3 +1,3 @@
-select id, counter_id, ts, value, arg_set_id, name, ref, ref_type from counters
+select ts, value, name, ref, ref_type from counters
where name = 'MemAvailable' and ref_type is null
limit 10
diff --git a/test/trace_processor/counters_ref_type_null_memory_counters.out b/test/trace_processor/counters_ref_type_null_memory_counters.out
index 0457994..d416b76 100644
--- a/test/trace_processor/counters_ref_type_null_memory_counters.out
+++ b/test/trace_processor/counters_ref_type_null_memory_counters.out
@@ -1,11 +1,11 @@
-"id","counter_id","ts","value","arg_set_id","name","ref","ref_type"
-4294968280,984,22240334823167,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294969268,984,22240356169836,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294970256,984,22240468594483,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294971244,984,22240566948190,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294972232,984,22240667383304,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294973220,984,22240766505085,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294974208,984,22240866794106,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294975196,984,22240968271928,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294976184,984,22241065777407,2696392704.000000,0,"MemAvailable",0,"[NULL]"
-4294977172,984,22241165839708,2696392704.000000,0,"MemAvailable",0,"[NULL]"
+"ts","value","name","ref","ref_type"
+22240334823167,2696392704.000000,"MemAvailable",0,"[NULL]"
+22240356169836,2696392704.000000,"MemAvailable",0,"[NULL]"
+22240468594483,2696392704.000000,"MemAvailable",0,"[NULL]"
+22240566948190,2696392704.000000,"MemAvailable",0,"[NULL]"
+22240667383304,2696392704.000000,"MemAvailable",0,"[NULL]"
+22240766505085,2696392704.000000,"MemAvailable",0,"[NULL]"
+22240866794106,2696392704.000000,"MemAvailable",0,"[NULL]"
+22240968271928,2696392704.000000,"MemAvailable",0,"[NULL]"
+22241065777407,2696392704.000000,"MemAvailable",0,"[NULL]"
+22241165839708,2696392704.000000,"MemAvailable",0,"[NULL]"
diff --git a/test/trace_processor/index b/test/trace_processor/index
index 958b11e..808b2cf 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -12,6 +12,7 @@
# Test for the process<>thread tracking logic.
synth_process_tracking.py process_tracking.sql process_tracking.out
+synth_process_tracking.py process_tracking_uid.sql process_tracking_uid.out
process_tracking_short_lived_1.py process_tracking.sql process_tracking_process_tracking_short_lived_1.out
process_tracking_short_lived_2.py process_tracking.sql process_tracking_process_tracking_short_lived_2.out
process_tracking_exec.py process_tracking.sql process_tracking_process_tracking_exec.out
diff --git a/test/trace_processor/mm_event.out b/test/trace_processor/mm_event.out
index a26a895..aee7eb1 100644
--- a/test/trace_processor/mm_event.out
+++ b/test/trace_processor/mm_event.out
@@ -1,41 +1,41 @@
-"id","ts","name","value","ref","ref_type","arg_set_id"
-4294967296,1409847208580693,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967297,1409847208580693,"mem.mm.min_flt.max_lat",9.000000,0,"upid",0
-4294967298,1409847208580693,"mem.mm.min_flt.avg_lat",9.000000,0,"upid",0
-4294967299,1409847209537724,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967300,1409847209537724,"mem.mm.min_flt.max_lat",759.000000,0,"upid",0
-4294967301,1409847209537724,"mem.mm.min_flt.avg_lat",759.000000,0,"upid",0
-4294967302,1409847213761006,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967303,1409847213761006,"mem.mm.min_flt.max_lat",10.000000,0,"upid",0
-4294967304,1409847213761006,"mem.mm.min_flt.avg_lat",10.000000,0,"upid",0
-4294967305,1409847214052256,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967306,1409847214052256,"mem.mm.min_flt.max_lat",90.000000,0,"upid",0
-4294967307,1409847214052256,"mem.mm.min_flt.avg_lat",90.000000,0,"upid",0
-4294967308,1409847216341006,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967309,1409847216341006,"mem.mm.min_flt.max_lat",961.000000,0,"upid",0
-4294967310,1409847216341006,"mem.mm.min_flt.avg_lat",961.000000,0,"upid",0
-4294967311,1409847216341944,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967312,1409847216341944,"mem.mm.min_flt.max_lat",889.000000,0,"upid",0
-4294967313,1409847216341944,"mem.mm.min_flt.avg_lat",889.000000,0,"upid",0
-4294967314,1409847218889548,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967315,1409847218889548,"mem.mm.min_flt.max_lat",10.000000,0,"upid",0
-4294967316,1409847218889548,"mem.mm.min_flt.avg_lat",10.000000,0,"upid",0
-4294967317,1409847219001371,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967318,1409847219001371,"mem.mm.min_flt.max_lat",66.000000,0,"upid",0
-4294967319,1409847219001371,"mem.mm.min_flt.avg_lat",66.000000,0,"upid",0
-4294967320,1409847352399457,"mem.mm.min_flt.count",12.000000,0,"upid",0
-4294967321,1409847352399457,"mem.mm.min_flt.max_lat",148.000000,0,"upid",0
-4294967322,1409847352399457,"mem.mm.min_flt.avg_lat",54.000000,0,"upid",0
-4294967323,1409847383618992,"mem.mm.min_flt.count",4.000000,0,"upid",0
-4294967324,1409847383618992,"mem.mm.min_flt.max_lat",34.000000,0,"upid",0
-4294967325,1409847383618992,"mem.mm.min_flt.avg_lat",21.000000,0,"upid",0
-4294967326,1409847431535611,"mem.mm.reclaim.count",1.000000,0,"upid",0
-4294967327,1409847431535611,"mem.mm.reclaim.max_lat",21.000000,0,"upid",0
-4294967328,1409847431535611,"mem.mm.reclaim.avg_lat",21.000000,0,"upid",0
-4294967329,1409847438281133,"mem.mm.compaction.count",1.000000,0,"upid",0
-4294967330,1409847438281133,"mem.mm.compaction.max_lat",6564.000000,0,"upid",0
-4294967331,1409847438281133,"mem.mm.compaction.avg_lat",6564.000000,0,"upid",0
-4294967332,1409847446501654,"mem.mm.min_flt.count",1.000000,0,"upid",0
-4294967333,1409847446501654,"mem.mm.min_flt.max_lat",12.000000,0,"upid",0
-4294967334,1409847446501654,"mem.mm.min_flt.avg_lat",12.000000,0,"upid",0
-4294967335,1409847447831498,"mem.mm.min_flt.count",1.000000,0,"upid",0
+"ts","name","value","ref","ref_type"
+1409847208580693,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847208580693,"mem.mm.min_flt.max_lat",9.000000,0,"upid"
+1409847208580693,"mem.mm.min_flt.avg_lat",9.000000,0,"upid"
+1409847209537724,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847209537724,"mem.mm.min_flt.max_lat",759.000000,0,"upid"
+1409847209537724,"mem.mm.min_flt.avg_lat",759.000000,0,"upid"
+1409847213761006,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847213761006,"mem.mm.min_flt.max_lat",10.000000,0,"upid"
+1409847213761006,"mem.mm.min_flt.avg_lat",10.000000,0,"upid"
+1409847214052256,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847214052256,"mem.mm.min_flt.max_lat",90.000000,0,"upid"
+1409847214052256,"mem.mm.min_flt.avg_lat",90.000000,0,"upid"
+1409847216341006,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847216341006,"mem.mm.min_flt.max_lat",961.000000,0,"upid"
+1409847216341006,"mem.mm.min_flt.avg_lat",961.000000,0,"upid"
+1409847216341944,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847216341944,"mem.mm.min_flt.max_lat",889.000000,0,"upid"
+1409847216341944,"mem.mm.min_flt.avg_lat",889.000000,0,"upid"
+1409847218889548,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847218889548,"mem.mm.min_flt.max_lat",10.000000,0,"upid"
+1409847218889548,"mem.mm.min_flt.avg_lat",10.000000,0,"upid"
+1409847219001371,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847219001371,"mem.mm.min_flt.max_lat",66.000000,0,"upid"
+1409847219001371,"mem.mm.min_flt.avg_lat",66.000000,0,"upid"
+1409847352399457,"mem.mm.min_flt.count",12.000000,0,"upid"
+1409847352399457,"mem.mm.min_flt.max_lat",148.000000,0,"upid"
+1409847352399457,"mem.mm.min_flt.avg_lat",54.000000,0,"upid"
+1409847383618992,"mem.mm.min_flt.count",4.000000,0,"upid"
+1409847383618992,"mem.mm.min_flt.max_lat",34.000000,0,"upid"
+1409847383618992,"mem.mm.min_flt.avg_lat",21.000000,0,"upid"
+1409847431535611,"mem.mm.reclaim.count",1.000000,0,"upid"
+1409847431535611,"mem.mm.reclaim.max_lat",21.000000,0,"upid"
+1409847431535611,"mem.mm.reclaim.avg_lat",21.000000,0,"upid"
+1409847438281133,"mem.mm.compaction.count",1.000000,0,"upid"
+1409847438281133,"mem.mm.compaction.max_lat",6564.000000,0,"upid"
+1409847438281133,"mem.mm.compaction.avg_lat",6564.000000,0,"upid"
+1409847446501654,"mem.mm.min_flt.count",1.000000,0,"upid"
+1409847446501654,"mem.mm.min_flt.max_lat",12.000000,0,"upid"
+1409847446501654,"mem.mm.min_flt.avg_lat",12.000000,0,"upid"
+1409847447831498,"mem.mm.min_flt.count",1.000000,0,"upid"
diff --git a/test/trace_processor/mm_event.sql b/test/trace_processor/mm_event.sql
index 187d0ff..e286c68 100644
--- a/test/trace_processor/mm_event.sql
+++ b/test/trace_processor/mm_event.sql
@@ -1,4 +1,4 @@
-select id, ts, name, value, ref, ref_type, arg_set_id
+select ts, name, value, ref, ref_type
from counters
where name like 'mem.mm.%'
order by ts
diff --git a/test/trace_processor/process_tracking_uid.out b/test/trace_processor/process_tracking_uid.out
new file mode 100644
index 0000000..ae90e8d
--- /dev/null
+++ b/test/trace_processor/process_tracking_uid.out
@@ -0,0 +1,6 @@
+"pid","uid"
+0,"[NULL]"
+10,1001
+20,1002
+30,"[NULL]"
+40,"[NULL]"
diff --git a/test/trace_processor/process_tracking_uid.sql b/test/trace_processor/process_tracking_uid.sql
new file mode 100644
index 0000000..de06399
--- /dev/null
+++ b/test/trace_processor/process_tracking_uid.sql
@@ -0,0 +1,3 @@
+select pid, uid
+from process
+order by pid;
diff --git a/test/trace_processor/synth_process_tracking.py b/test/trace_processor/synth_process_tracking.py
index 6686d89..a7a4d2d 100644
--- a/test/trace_processor/synth_process_tracking.py
+++ b/test/trace_processor/synth_process_tracking.py
@@ -40,7 +40,7 @@
# SQL level we should be able to tell that p1-t0 and p1-t2 belong to 'process1'
# but p1-t1 should be left unjoinable.
trace.add_process_tree_packet(ts=5)
-trace.add_process(10, 0, "process1")
+trace.add_process(10, 0, "process1", 1001)
trace.add_thread(12, 10, "p1-t2")
# Now create another process (pid=20) with three threads(tids=20,21,22).
@@ -58,7 +58,7 @@
# From the process tracker viewpoint we pretend we only scraped tids=20,21.
trace.add_process_tree_packet(ts=15)
-trace.add_process(20, 0, "process_2")
+trace.add_process(20, 0, "process_2", 1002)
trace.add_thread(21, 20, "p2-t1")
# Finally the very complex case: a third process (pid=30) which spawns threads
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 7edcbba..8c5fb67 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -302,7 +302,8 @@
//}
const maxCpuFreq = await engine.query(`
select max(value)
- from counters
+ from counter c
+ inner join cpu_counter_track t on c.track_id = t.id
where name = 'cpufreq';
`);
@@ -325,13 +326,28 @@
// cpu freq data.
// TODO(taylori): Find a way to display cpu idle
// events even if there are no cpu freq events.
- const freqExists = await engine.query(`
- select value
- from counters
- where name = 'cpufreq' and ref = ${cpu}
+ const cpuFreqIdle = await engine.query(`
+ select
+ id as cpu_freq_id,
+ (
+ select id
+ from cpu_counter_track
+ where name = 'cpuidle'
+ and cpu = ${cpu}
+ limit 1
+ ) as cpu_idle_id
+ from cpu_counter_track
+ where name = 'cpufreq' and cpu = ${cpu}
limit 1;
`);
- if (freqExists.numRecords > 0) {
+ if (cpuFreqIdle.numRecords > 0) {
+ const freqTrackId = +cpuFreqIdle.columns[0].longValues![0];
+
+ const idleTrackExists: boolean = !cpuFreqIdle.columns[1].isNulls![0];
+ const idleTrackId = idleTrackExists ?
+ +cpuFreqIdle.columns[1].longValues![0] :
+ undefined;
+
tracksToAdd.push({
engineId: this.engineId,
kind: CPU_FREQ_TRACK_KIND,
@@ -340,6 +356,8 @@
config: {
cpu,
maximumValue: +maxCpuFreq.columns[0].doubleValues![0],
+ freqTrackId,
+ idleTrackId,
}
});
}
@@ -414,46 +432,69 @@
}
}
-
- const counters = await engine.query(`
- select name, ref, ref_type
- from counter_definitions
- where ref is not null
- group by name, ref, ref_type
- order by ref_type desc
+ // Add global or GPU counter tracks that are not bound to any pid/tid.
+ const globalCounters = await engine.query(`
+ select name, id
+ from counter_track
+ where type = 'counter_track'
+ union
+ select name, id
+ from gpu_counter_track
+ where name != 'gpufreq'
`);
-
- interface CounterMap {
- [index: number]: string[];
+ for (let i = 0; i < globalCounters.numRecords; i++) {
+ const name = globalCounters.columns[0].stringValues![i];
+ const trackId = +globalCounters.columns[1].longValues![i];
+ tracksToAdd.push({
+ engineId: this.engineId,
+ kind: 'CounterTrack',
+ name,
+ trackGroup: SCROLLING_TRACK_GROUP,
+ config: {
+ name,
+ trackId,
+ }
+ });
}
- const counterUpids: CounterMap = new Array();
- const counterUtids: CounterMap = new Array();
- for (let i = 0; i < counters.numRecords; i++) {
- const name = counters.columns[0].stringValues![i];
- const ref = +counters.columns[1].longValues![i];
- const refType = counters.columns[2].stringValues![i];
- if (refType === 'upid') {
- const el = counterUpids[ref];
- el === undefined ? counterUpids[ref] = [name] :
- counterUpids[ref].push(name);
- } else if (refType === 'utid') {
- const el = counterUtids[ref];
- el === undefined ? counterUtids[ref] = [name] :
- counterUtids[ref].push(name);
- } else if (
- refType === '[NULL]' || (refType === 'gpu' && name !== 'gpufreq')) {
- // Add global or GPU counter tracks that are not bound to any pid/tid.
- tracksToAdd.push({
- engineId: this.engineId,
- kind: 'CounterTrack',
- name,
- trackGroup: SCROLLING_TRACK_GROUP,
- config: {
- name,
- ref: 0,
- }
- });
+ interface CounterTrack {
+ name: string;
+ trackId: number;
+ }
+
+ const counterUtids = new Map<number, CounterTrack[]>();
+ const threadCounters = await engine.query(`
+ select name, utid, id
+ from thread_counter_track
+ `);
+ for (let i = 0; i < threadCounters.numRecords; i++) {
+ const name = threadCounters.columns[0].stringValues![i];
+ const utid = +threadCounters.columns[1].longValues![i];
+ const trackId = +threadCounters.columns[2].longValues![i];
+
+ const el = counterUtids.get(utid);
+ if (el === undefined) {
+ counterUtids.set(utid, [{name, trackId}]);
+ } else {
+ el.push({name, trackId});
+ }
+ }
+
+ const counterUpids = new Map<number, CounterTrack[]>();
+ const processCounters = await engine.query(`
+ select name, upid, id
+ from process_counter_track
+ `);
+ for (let i = 0; i < processCounters.numRecords; i++) {
+ const name = processCounters.columns[0].stringValues![i];
+ const upid = +processCounters.columns[1].longValues![i];
+ const trackId = +processCounters.columns[2].longValues![i];
+
+ const el = counterUpids.get(upid);
+ if (el === undefined) {
+ counterUpids.set(upid, [{name, trackId}]);
+ } else {
+ el.push({name, trackId});
}
}
@@ -526,8 +567,8 @@
const threadTrack =
utid === null ? undefined : utidToThreadTrack.get(utid);
if (threadTrack === undefined &&
- (upid === null || counterUpids[upid] === undefined) &&
- counterUtids[utid] === undefined && !threadHasSched &&
+ (upid === null || counterUpids.get(upid) === undefined) &&
+ counterUtids.get(utid) === undefined && !threadHasSched &&
(upid === null || upid !== null && !heapUpids.has(upid))) {
continue;
}
@@ -571,18 +612,15 @@
}));
if (upid !== null) {
- const counterNames = counterUpids[upid];
+ const counterNames = counterUpids.get(upid);
if (counterNames !== undefined) {
counterNames.forEach(element => {
tracksToAdd.push({
engineId: this.engineId,
kind: 'CounterTrack',
- name: element,
+ name: element.name,
trackGroup: pUuid,
- config: {
- name: element,
- ref: upid,
- }
+ config: {name: element.name, trackId: element.trackId}
});
});
}
@@ -612,17 +650,17 @@
}
}
}
- const counterThreadNames = counterUtids[utid];
+ const counterThreadNames = counterUtids.get(utid);
if (counterThreadNames !== undefined) {
counterThreadNames.forEach(element => {
tracksToAdd.push({
engineId: this.engineId,
kind: 'CounterTrack',
- name: element,
+ name: element.name,
trackGroup: pUuid,
config: {
- name: element,
- ref: utid,
+ name: element.name,
+ trackId: element.trackId,
}
});
});
diff --git a/ui/src/tracks/counter/common.ts b/ui/src/tracks/counter/common.ts
index 0eb6dfb..53a603d 100644
--- a/ui/src/tracks/counter/common.ts
+++ b/ui/src/tracks/counter/common.ts
@@ -30,6 +30,6 @@
name: string;
maximumValue?: number;
minimumValue?: number;
- ref: number;
+ trackId: number;
scale?: 'DEFAULT'|'RELATIVE';
}
diff --git a/ui/src/tracks/counter/controller.ts b/ui/src/tracks/counter/controller.ts
index 4085845..0be0371 100644
--- a/ui/src/tracks/counter/controller.ts
+++ b/ui/src/tracks/counter/controller.ts
@@ -39,20 +39,22 @@
if (!this.setup) {
const result = await this.query(`
- select max(value), min(value) from
- counters where name = '${this.config.name}'
- and ref = ${this.config.ref}`);
+ select max(value), min(value)
+ from counter
+ where track_id = ${this.config.trackId}`);
this.maximumValueSeen = +result.columns[0].doubleValues![0];
this.minimumValueSeen = +result.columns[1].doubleValues![0];
await this.query(
`create virtual table ${this.tableName('window')} using window;`);
- await this.query(`create view ${this.tableName('counter_view')} as
- select ts,
- lead(ts, 1, ts) over (partition by ref_type order by ts) - ts as dur,
- value, name, ref
- from counters
- where name = '${this.config.name}' and ref = ${this.config.ref};`);
+ await this.query(`
+ create view ${this.tableName('counter_view')} as
+ select
+ ts,
+ lead(ts, 1, ts) over (order by ts) - ts as dur,
+ value
+ from counter
+ where track_id = ${this.config.trackId};`);
await this.query(`create virtual table ${this.tableName('span')} using
span_join(${this.tableName('counter_view')},
@@ -60,13 +62,16 @@
this.setup = true;
}
- const result = await this.engine.queryOneRow(`select count(*)
- from (select
- ts,
- lead(ts, 1, ts) over (partition by ref_type order by ts) as ts_end,
- from counters
- where name = '${this.config.name}' and ref = ${this.config.ref})
- where ts <= ${endNs} and ${startNs} <= ts_end`);
+ const result = await this.engine.queryOneRow(`
+ select count(*)
+ from (
+ select
+ ts,
+ lead(ts, 1, ts) over (order by ts) as ts_end,
+ from counter
+ where track_id = ${this.config.trackId}
+ )
+ where ts <= ${endNs} and ${startNs} <= ts_end`);
// Only quantize if we have too much data to draw.
const isQuantized = result[0] > LIMIT;
@@ -94,18 +99,32 @@
// Union that with the query that finds all the counters within
// the current query range.
query = `
- select * from (select ts, value, counter_id from counters
- where name = '${this.config.name}' and ref = ${this.config.ref} and
- ts <= ${startNs} order by ts desc limit 1)
- UNION
- select * from (select ts, value, counter_id
- from (select
- ts,
- lead(ts, 1, ts) over (partition by ref_type order by ts) as ts_end,
- value, counter_id
- from counters
- where name = '${this.config.name}' and ref = ${this.config.ref})
- where ts <= ${endNs} and ${startNs} <= ts_end limit ${LIMIT});`;
+ select *
+ from (
+ select ts, value, track_id
+ from counter
+ where
+ track_id = ${this.config.trackId} and
+ ts <= ${startNs}
+ order by ts desc
+ limit 1
+ )
+ union
+ select *
+ from (
+ select ts, value, track_id
+ from (
+ select
+ ts,
+ lead(ts, 1, ts) over (order by ts) as ts_end,
+ value,
+ track_id
+ from counter
+ where track_id = ${this.config.trackId}
+ )
+ where ts <= ${endNs} and ${startNs} <= ts_end
+ limit ${LIMIT}
+ );`;
}
const rawResult = await this.query(query);
diff --git a/ui/src/tracks/cpu_freq/common.ts b/ui/src/tracks/cpu_freq/common.ts
index 8968907..fff0c8f 100644
--- a/ui/src/tracks/cpu_freq/common.ts
+++ b/ui/src/tracks/cpu_freq/common.ts
@@ -28,5 +28,8 @@
export interface Config {
cpu: number;
+ freqTrackId: number;
+ idleTrackId?: number;
maximumValue?: number;
- minimumValue?: number;}
+ minimumValue?: number;
+}
diff --git a/ui/src/tracks/cpu_freq/controller.ts b/ui/src/tracks/cpu_freq/controller.ts
index 190a3d9..bdfbdae 100644
--- a/ui/src/tracks/cpu_freq/controller.ts
+++ b/ui/src/tracks/cpu_freq/controller.ts
@@ -38,9 +38,9 @@
if (!this.setup) {
const result = await this.query(`
- select max(value) from
- counters where name = 'cpufreq'
- and ref = ${this.config.cpu}`);
+ select max(value)
+ from counter
+ where track_id = ${this.config.freqTrackId}`);
this.maximumValueSeen = +result.columns[0].doubleValues![0];
await this.query(
@@ -49,36 +49,39 @@
await this.query(`create view ${this.tableName('freq')}
as select
ts,
- lead(ts) over (order by ts) - ts as dur,
- ref as cpu,
- name as freq_name,
+ lead(ts) over () - ts as dur,
value as freq_value
- from counters
- where name = 'cpufreq'
- and ref = ${this.config.cpu}
- and ref_type = 'cpu';
+ from counter c
+ where track_id = ${this.config.freqTrackId};
`);
- await this.query(`create view ${this.tableName('idle')}
- as select
- ts,
- lead(ts) over (order by ts) - ts as dur,
- ref as cpu,
- name as idle_name,
- value as idle_value
- from counters
- where name = 'cpuidle'
- and ref = ${this.config.cpu}
- and ref_type = 'cpu';
- `);
+ // If there is no idle track, just make the idle track a single row
+ // which spans the entire time range.
+ if (this.config.idleTrackId === undefined) {
+ await this.query(`create view ${this.tableName('idle')} as
+ select
+ 0 as ts,
+ ${Number.MAX_SAFE_INTEGER} as dur,
+ -1 as idle_value;
+ `);
+ } else {
+ await this.query(`create view ${this.tableName('idle')}
+ as select
+ ts,
+ lead(ts) over () - ts as dur,
+ value as idle_value
+ from counter c
+ where track_id = ${this.config.idleTrackId};
+ `);
+ }
await this.query(`create virtual table ${this.tableName('freq_idle')}
- using span_join(${this.tableName('freq')} PARTITIONED cpu,
- ${this.tableName('idle')} PARTITIONED cpu);`);
+ using span_join(${this.tableName('freq')},
+ ${this.tableName('idle')});`);
await this.query(`create virtual table ${this.tableName('span_activity')}
- using span_join(${this.tableName('freq_idle')} PARTITIONED cpu,
- ${this.tableName('window')});`);
+ using span_join(${this.tableName('freq_idle')},
+ ${this.tableName('window')});`);
// TODO(taylori): Move the idle value processing to the TP.
await this.query(`create view ${this.tableName('activity')}
@@ -86,7 +89,6 @@
ts,
dur,
quantum_ts,
- cpu,
case idle_value
when 4294967295 then -1
else idle_value