Merge "Add proposed trendy teams for CTS modules" into main
diff --git a/Android.bp b/Android.bp
index 9aa955d..8b07472 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2391,6 +2391,7 @@
":perfetto_src_shared_lib_test_utils",
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_db_column_column",
+ ":perfetto_src_trace_processor_db_compare",
":perfetto_src_trace_processor_db_db",
":perfetto_src_trace_processor_db_minimal",
":perfetto_src_trace_processor_export_json",
@@ -2424,6 +2425,7 @@
":perfetto_src_trace_processor_importers_systrace_full",
":perfetto_src_trace_processor_importers_systrace_systrace_line",
":perfetto_src_trace_processor_importers_systrace_systrace_parser",
+ ":perfetto_src_trace_processor_importers_zip_full",
":perfetto_src_trace_processor_lib",
":perfetto_src_trace_processor_metatrace",
":perfetto_src_trace_processor_metrics_metrics",
@@ -12561,6 +12563,14 @@
],
}
+// GN: //src/trace_processor/importers/zip:full
+filegroup {
+ name: "perfetto_src_trace_processor_importers_zip_full",
+ srcs: [
+ "src/trace_processor/importers/zip/zip_trace_reader.cc",
+ ],
+}
+
// GN: //src/trace_processor:lib
filegroup {
name: "perfetto_src_trace_processor_lib",
@@ -15089,6 +15099,7 @@
":perfetto_src_trace_processor_importers_systrace_systrace_line",
":perfetto_src_trace_processor_importers_systrace_systrace_parser",
":perfetto_src_trace_processor_importers_systrace_unittests",
+ ":perfetto_src_trace_processor_importers_zip_full",
":perfetto_src_trace_processor_lib",
":perfetto_src_trace_processor_metatrace",
":perfetto_src_trace_processor_metrics_metrics",
@@ -16052,6 +16063,7 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_db_column_column",
+ ":perfetto_src_trace_processor_db_compare",
":perfetto_src_trace_processor_db_db",
":perfetto_src_trace_processor_db_minimal",
":perfetto_src_trace_processor_export_json",
@@ -16085,6 +16097,7 @@
":perfetto_src_trace_processor_importers_systrace_full",
":perfetto_src_trace_processor_importers_systrace_systrace_line",
":perfetto_src_trace_processor_importers_systrace_systrace_parser",
+ ":perfetto_src_trace_processor_importers_zip_full",
":perfetto_src_trace_processor_lib",
":perfetto_src_trace_processor_metatrace",
":perfetto_src_trace_processor_metrics_metrics",
@@ -16288,6 +16301,7 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_db_column_column",
+ ":perfetto_src_trace_processor_db_compare",
":perfetto_src_trace_processor_db_minimal",
":perfetto_src_trace_processor_importers_common_common",
":perfetto_src_trace_processor_importers_common_parser_types",
@@ -16447,6 +16461,7 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_db_column_column",
+ ":perfetto_src_trace_processor_db_compare",
":perfetto_src_trace_processor_db_db",
":perfetto_src_trace_processor_db_minimal",
":perfetto_src_trace_processor_export_json",
@@ -16480,6 +16495,7 @@
":perfetto_src_trace_processor_importers_systrace_full",
":perfetto_src_trace_processor_importers_systrace_systrace_line",
":perfetto_src_trace_processor_importers_systrace_systrace_parser",
+ ":perfetto_src_trace_processor_importers_zip_full",
":perfetto_src_trace_processor_lib",
":perfetto_src_trace_processor_metatrace",
":perfetto_src_trace_processor_metrics_metrics",
diff --git a/BUILD b/BUILD
index fa45cad..537ccc4 100644
--- a/BUILD
+++ b/BUILD
@@ -217,6 +217,7 @@
":src_kernel_utils_syscall_table",
":src_protozero_proto_ring_buffer",
":src_trace_processor_db_column_column",
+ ":src_trace_processor_db_compare",
":src_trace_processor_db_db",
":src_trace_processor_db_minimal",
":src_trace_processor_export_json",
@@ -250,6 +251,7 @@
":src_trace_processor_importers_systrace_full",
":src_trace_processor_importers_systrace_systrace_line",
":src_trace_processor_importers_systrace_systrace_parser",
+ ":src_trace_processor_importers_zip_full",
":src_trace_processor_lib",
":src_trace_processor_metatrace",
":src_trace_processor_metrics_metrics",
@@ -1425,6 +1427,14 @@
],
)
+# GN target: //src/trace_processor/db:compare
+perfetto_filegroup(
+ name = "src_trace_processor_db_compare",
+ srcs = [
+ "src/trace_processor/db/compare.h",
+ ],
+)
+
# GN target: //src/trace_processor/db:db
perfetto_filegroup(
name = "src_trace_processor_db_db",
@@ -2004,6 +2014,15 @@
],
)
+# GN target: //src/trace_processor/importers/zip:full
+perfetto_filegroup(
+ name = "src_trace_processor_importers_zip_full",
+ srcs = [
+ "src/trace_processor/importers/zip/zip_trace_reader.cc",
+ "src/trace_processor/importers/zip/zip_trace_reader.h",
+ ],
+)
+
# GN target: //src/trace_processor/metrics/sql/android:android
perfetto_filegroup(
name = "src_trace_processor_metrics_sql_android_android",
@@ -5969,6 +5988,7 @@
srcs = [
":src_kernel_utils_syscall_table",
":src_trace_processor_db_column_column",
+ ":src_trace_processor_db_compare",
":src_trace_processor_db_db",
":src_trace_processor_db_minimal",
":src_trace_processor_export_json",
@@ -6002,6 +6022,7 @@
":src_trace_processor_importers_systrace_full",
":src_trace_processor_importers_systrace_systrace_line",
":src_trace_processor_importers_systrace_systrace_parser",
+ ":src_trace_processor_importers_zip_full",
":src_trace_processor_lib",
":src_trace_processor_metatrace",
":src_trace_processor_metrics_metrics",
@@ -6147,6 +6168,7 @@
":src_profiling_symbolizer_symbolizer",
":src_protozero_proto_ring_buffer",
":src_trace_processor_db_column_column",
+ ":src_trace_processor_db_compare",
":src_trace_processor_db_db",
":src_trace_processor_db_minimal",
":src_trace_processor_export_json",
@@ -6180,6 +6202,7 @@
":src_trace_processor_importers_systrace_full",
":src_trace_processor_importers_systrace_systrace_line",
":src_trace_processor_importers_systrace_systrace_parser",
+ ":src_trace_processor_importers_zip_full",
":src_trace_processor_lib",
":src_trace_processor_metatrace",
":src_trace_processor_metrics_metrics",
@@ -6385,6 +6408,7 @@
":src_profiling_symbolizer_symbolizer",
":src_protozero_proto_ring_buffer",
":src_trace_processor_db_column_column",
+ ":src_trace_processor_db_compare",
":src_trace_processor_db_db",
":src_trace_processor_db_minimal",
":src_trace_processor_export_json",
@@ -6418,6 +6442,7 @@
":src_trace_processor_importers_systrace_full",
":src_trace_processor_importers_systrace_systrace_line",
":src_trace_processor_importers_systrace_systrace_parser",
+ ":src_trace_processor_importers_zip_full",
":src_trace_processor_lib",
":src_trace_processor_metatrace",
":src_trace_processor_metrics_metrics",
diff --git a/CHANGELOG b/CHANGELOG
index 70cb79c..8aae7f5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,10 +1,13 @@
Unreleased:
Tracing service and probes:
- *
+ *
SQL Standard library:
* Added megacycles support to CPU package. Added tables:
`cpu_cycles_per_process`, `cpu_cycles_per_thread` and
`cpu_cycles_per_cpu`.
+ * Improved `memory` package. Added `memory.linux.process`,
+ `memory.linux.high_watermark` and `memory.android.gpu` modules.
+ * Created `gpu` package with `gpu.frequency` module.
* Migrated `sched.utilization` package to `cpu.utilization`.
Trace Processor:
* Added "time to initial display" and "time to full display" metrics to
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index 66f01ec..4a93ebc 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -342,7 +342,7 @@
optional uint32 thread_utid = 3;
}
- // Next id: 24
+ // Next id: 25
message Startup {
// Random id uniquely identifying an app startup in this trace.
optional uint32 startup_id = 1;
@@ -424,9 +424,16 @@
// Optional.
repeated string slow_start_reason = 17;
- // Same as slow_start_reason, but with more detailed information.
+ // Same as slow_start_reason, but with more detailed information, obsolete.
repeated SlowStartReasonDetailed slow_start_reason_detailed = 21;
+ // Similar to slow_start_reason_detailed, but with much more comprehensive info.
+ // such as expected threshold, actual value and threads/slices to inspect.
+ // slow_start_reason will be obsolete once slow_start_reason_with_details is completed
+ // since slow_start_reason_with_details contains all the data in slow_start_reason
+ // and more.
+ repeated SlowStartReason slow_start_reason_with_details = 24;
+
reserved 10;
}
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index bffca26..ad2d6b2 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -2458,7 +2458,7 @@
optional uint32 thread_utid = 3;
}
- // Next id: 24
+ // Next id: 25
message Startup {
// Random id uniquely identifying an app startup in this trace.
optional uint32 startup_id = 1;
@@ -2540,9 +2540,16 @@
// Optional.
repeated string slow_start_reason = 17;
- // Same as slow_start_reason, but with more detailed information.
+ // Same as slow_start_reason, but with more detailed information, obsolete.
repeated SlowStartReasonDetailed slow_start_reason_detailed = 21;
+ // Similar to slow_start_reason_detailed, but with much more comprehensive info.
+ // such as expected threshold, actual value and threads/slices to inspect.
+ // slow_start_reason will be obsolete once slow_start_reason_with_details is completed
+ // since slow_start_reason_with_details contains all the data in slow_start_reason
+ // and more.
+ repeated SlowStartReason slow_start_reason_with_details = 24;
+
reserved 10;
}
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index ef13816..f212c89 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 2a61ec6..802f9bc 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -177,6 +177,7 @@
"importers/proto:full",
"importers/proto:minimal",
"importers/systrace:full",
+ "importers/zip:full",
"metrics",
"perfetto_sql/engine",
"perfetto_sql/intrinsics/functions",
diff --git a/src/trace_processor/containers/string_pool.h b/src/trace_processor/containers/string_pool.h
index 75a57a2..2e03592 100644
--- a/src/trace_processor/containers/string_pool.h
+++ b/src/trace_processor/containers/string_pool.h
@@ -17,21 +17,25 @@
#ifndef SRC_TRACE_PROCESSOR_CONTAINERS_STRING_POOL_H_
#define SRC_TRACE_PROCESSOR_CONTAINERS_STRING_POOL_H_
-#include <stddef.h>
-#include <stdint.h>
-
+#include <cstddef>
+#include <cstdint>
+#include <functional>
#include <limits>
+#include <memory>
#include <optional>
+#include <string>
+#include <utility>
#include <vector>
+#include "perfetto/base/logging.h"
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/hash.h"
#include "perfetto/ext/base/paged_memory.h"
+#include "perfetto/ext/base/string_view.h"
#include "perfetto/protozero/proto_utils.h"
#include "src/trace_processor/containers/null_term_string_view.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
// Interns strings in a string pool and hands out compact StringIds which can
// be used to retrieve the string in O(1).
@@ -40,34 +44,38 @@
struct Id {
Id() = default;
- bool operator==(const Id& other) const { return other.id == id; }
- bool operator!=(const Id& other) const { return !(other == *this); }
- bool operator<(const Id& other) const { return id < other.id; }
+ constexpr bool operator==(const Id& other) const { return other.id == id; }
+ constexpr bool operator!=(const Id& other) const {
+ return !(other == *this);
+ }
+ constexpr bool operator<(const Id& other) const { return id < other.id; }
- bool is_null() const { return id == 0u; }
+ constexpr bool is_null() const { return id == 0u; }
- bool is_large_string() const { return id & kLargeStringFlagBitMask; }
+ constexpr bool is_large_string() const {
+ return id & kLargeStringFlagBitMask;
+ }
- uint32_t block_offset() const { return id & kBlockOffsetBitMask; }
+ constexpr uint32_t block_offset() const { return id & kBlockOffsetBitMask; }
- uint32_t block_index() const {
+ constexpr uint32_t block_index() const {
return (id & kBlockIndexBitMask) >> kNumBlockOffsetBits;
}
- uint32_t large_string_index() const {
+ constexpr uint32_t large_string_index() const {
PERFETTO_DCHECK(is_large_string());
return id & ~kLargeStringFlagBitMask;
}
- uint32_t raw_id() const { return id; }
+ constexpr uint32_t raw_id() const { return id; }
- static Id LargeString(size_t index) {
+ static constexpr Id LargeString(size_t index) {
PERFETTO_DCHECK(index <= static_cast<uint32_t>(index));
PERFETTO_DCHECK(!(index & kLargeStringFlagBitMask));
return Id(kLargeStringFlagBitMask | static_cast<uint32_t>(index));
}
- static Id BlockString(size_t index, uint32_t offset) {
+ static constexpr Id BlockString(size_t index, uint32_t offset) {
PERFETTO_DCHECK(index < (1u << (kNumBlockIndexBits + 1)));
PERFETTO_DCHECK(offset < (1u << (kNumBlockOffsetBits + 1)));
return Id(~kLargeStringFlagBitMask &
@@ -80,7 +88,7 @@
static constexpr Id Null() { return Id(0u); }
private:
- constexpr Id(uint32_t i) : id(i) {}
+ constexpr explicit Id(uint32_t i) : id(i) {}
uint32_t id;
};
@@ -88,7 +96,7 @@
// Iterator over the strings in the pool.
class Iterator {
public:
- Iterator(const StringPool*);
+ explicit Iterator(const StringPool*);
explicit operator bool() const;
Iterator& operator++();
@@ -278,7 +286,7 @@
static NullTermStringView GetFromBlockPtr(const uint8_t* ptr) {
uint32_t size = 0;
const uint8_t* str_ptr = ReadSize(ptr, &size);
- return NullTermStringView(reinterpret_cast<const char*>(str_ptr), size);
+ return {reinterpret_cast<const char*>(str_ptr), size};
}
// Lookup a string in the |large_strings_| vector. |id| should have the MSB
@@ -288,7 +296,7 @@
size_t index = id.large_string_index();
PERFETTO_DCHECK(index < large_strings_.size());
const std::string* str = large_strings_[index].get();
- return NullTermStringView(str->c_str(), str->size());
+ return {str->c_str(), str->size()};
}
// The actual memory storing the strings.
@@ -308,8 +316,7 @@
string_index_{/*initial_capacity=*/4096u};
};
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
template <>
struct std::hash<::perfetto::trace_processor::StringPool::Id> {
diff --git a/src/trace_processor/db/column/BUILD.gn b/src/trace_processor/db/column/BUILD.gn
index d53e7ae..f77887f 100644
--- a/src/trace_processor/db/column/BUILD.gn
+++ b/src/trace_processor/db/column/BUILD.gn
@@ -43,6 +43,7 @@
"utils.h",
]
deps = [
+ "..:compare",
"../..:metatrace",
"../../../../gn:default_deps",
"../../../../include/perfetto/trace_processor",
diff --git a/src/trace_processor/db/column/arrangement_overlay.cc b/src/trace_processor/db/column/arrangement_overlay.cc
index c31d7c2..e7bc8ff 100644
--- a/src/trace_processor/db/column/arrangement_overlay.cc
+++ b/src/trace_processor/db/column/arrangement_overlay.cc
@@ -68,12 +68,21 @@
if (does_arrangement_order_storage_ && op != FilterOp::kGlob &&
op != FilterOp::kRegex) {
- Range inner_res = inner_->OrderedIndexSearchValidated(
- op, sql_val,
- OrderedIndices{arrangement_->data() + in.start, in.size(),
- arrangement_state_});
+ OrderedIndices indices{arrangement_->data() + in.start, in.size(),
+ arrangement_state_};
+ if (op == FilterOp::kNe) {
+ // Do an equality search and "invert" the range.
+ Range inner_res =
+ inner_->OrderedIndexSearchValidated(FilterOp::kEq, sql_val, indices);
+ BitVector bv(in.start);
+ bv.Resize(in.start + inner_res.start, true);
+ bv.Resize(in.start + inner_res.end, false);
+ bv.Resize(in.end, true);
+ return RangeOrBitVector(std::move(bv));
+ }
+ Range inner_res = inner_->OrderedIndexSearchValidated(op, sql_val, indices);
return RangeOrBitVector(
- Range(inner_res.start + in.start, inner_res.end + in.start));
+ Range(in.start + inner_res.start, in.start + inner_res.end));
}
const auto& arrangement = *arrangement_;
@@ -196,6 +205,11 @@
return inner_->MinElement(indices);
}
+SqlValue ArrangementOverlay::ChainImpl::Get_AvoidUsingBecauseSlow(
+ uint32_t index) const {
+ return inner_->Get_AvoidUsingBecauseSlow((*arrangement_)[index]);
+}
+
void ArrangementOverlay::ChainImpl::Serialize(StorageProto* storage) const {
auto* arrangement_overlay = storage->set_arrangement_overlay();
arrangement_overlay->set_values(
diff --git a/src/trace_processor/db/column/arrangement_overlay.h b/src/trace_processor/db/column/arrangement_overlay.h
index 3656b07..85b9ce4 100644
--- a/src/trace_processor/db/column/arrangement_overlay.h
+++ b/src/trace_processor/db/column/arrangement_overlay.h
@@ -19,10 +19,10 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
-#include "perfetto/base/logging.h"
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/db/column/data_layer.h"
#include "src/trace_processor/db/column/types.h"
@@ -61,13 +61,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override {
- PERFETTO_FATAL(
- "OrderedIndexSearch can't be called on ArrangementOverlay");
- }
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -78,6 +71,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override {
diff --git a/src/trace_processor/db/column/arrangement_overlay_unittest.cc b/src/trace_processor/db/column/arrangement_overlay_unittest.cc
index 55f310b..8820336 100644
--- a/src/trace_processor/db/column/arrangement_overlay_unittest.cc
+++ b/src/trace_processor/db/column/arrangement_overlay_unittest.cc
@@ -108,15 +108,20 @@
}
TEST(ArrangementOverlay, OrderingSearch) {
- std::vector<uint32_t> arrangement{0, 2, 4, 1, 3};
- auto fake = FakeStorageChain::SearchSubset(5, BitVector({0, 1, 0, 1, 0}));
+ std::vector<uint32_t> numeric_data{0, 1, 2, 0, 1, 0};
+ NumericStorage<uint32_t> numeric(&numeric_data, ColumnType::kUint32, false);
+
+ std::vector<uint32_t> arrangement{0, 3, 5, 1, 4, 2};
ArrangementOverlay storage(&arrangement, Indices::State::kNonmonotonic);
- auto chain =
- storage.MakeChain(std::move(fake), DataLayer::ChainCreationArgs(true));
+ auto chain = storage.MakeChain(numeric.MakeChain(),
+ DataLayer::ChainCreationArgs(true));
RangeOrBitVector res =
- chain->Search(FilterOp::kGe, SqlValue::Long(0u), Range(0, 5));
+ chain->Search(FilterOp::kGe, SqlValue::Long(1u), Range(0, 5));
ASSERT_THAT(utils::ToIndexVectorForTests(res), ElementsAre(3, 4));
+
+ res = chain->Search(FilterOp::kNe, SqlValue::Long(1u), Range(1, 6));
+ ASSERT_THAT(utils::ToIndexVectorForTests(res), ElementsAre(1, 2, 5));
}
TEST(ArrangementOverlay, StableSort) {
diff --git a/src/trace_processor/db/column/data_layer.cc b/src/trace_processor/db/column/data_layer.cc
index 634cbce..a570415 100644
--- a/src/trace_processor/db/column/data_layer.cc
+++ b/src/trace_processor/db/column/data_layer.cc
@@ -16,12 +16,15 @@
#include "src/trace_processor/db/column/data_layer.h"
+#include <algorithm>
#include <cstdint>
+#include <iterator>
#include <memory>
#include <utility>
#include <vector>
#include "perfetto/base/logging.h"
+#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/db/column/arrangement_overlay.h"
@@ -35,6 +38,7 @@
#include "src/trace_processor/db/column/set_id_storage.h"
#include "src/trace_processor/db/column/string_storage.h"
#include "src/trace_processor/db/column/types.h"
+#include "src/trace_processor/db/compare.h"
namespace perfetto::trace_processor::column {
@@ -113,6 +117,53 @@
PERFETTO_FATAL("For GCC");
}
+Range DataLayerChain::OrderedIndexSearchValidated(
+ FilterOp op,
+ SqlValue value,
+ const OrderedIndices& indices) const {
+ auto lb = [&]() {
+ return static_cast<uint32_t>(std::distance(
+ indices.data,
+ std::lower_bound(indices.data, indices.data + indices.size, value,
+ [this](uint32_t idx, const SqlValue& v) {
+ return compare::SqlValueComparator(
+ Get_AvoidUsingBecauseSlow(idx), v);
+ })));
+ };
+ auto ub = [&]() {
+ return static_cast<uint32_t>(std::distance(
+ indices.data,
+ std::upper_bound(indices.data, indices.data + indices.size, value,
+ [this](const SqlValue& v, uint32_t idx) {
+ return compare::SqlValueComparator(
+ v, Get_AvoidUsingBecauseSlow(idx));
+ })));
+ };
+ switch (op) {
+ case FilterOp::kEq:
+ return {lb(), ub()};
+ case FilterOp::kLe:
+ return {0, ub()};
+ case FilterOp::kLt:
+ return {0, lb()};
+ case FilterOp::kGe:
+ return {lb(), indices.size};
+ case FilterOp::kGt:
+ return {ub(), indices.size};
+ case FilterOp::kIsNull:
+ PERFETTO_CHECK(value.is_null());
+ return {0, ub()};
+ case FilterOp::kIsNotNull:
+ PERFETTO_CHECK(value.is_null());
+ return {ub(), indices.size};
+ case FilterOp::kNe:
+ case FilterOp::kGlob:
+ case FilterOp::kRegex:
+ PERFETTO_FATAL("Wrong filtering operation");
+ }
+ PERFETTO_FATAL("For GCC");
+}
+
ArrangementOverlay::ArrangementOverlay(
const std::vector<uint32_t>* arrangement,
DataLayerChain::Indices::State arrangement_state)
diff --git a/src/trace_processor/db/column/data_layer.h b/src/trace_processor/db/column/data_layer.h
index 5e34539..b6f9382 100644
--- a/src/trace_processor/db/column/data_layer.h
+++ b/src/trace_processor/db/column/data_layer.h
@@ -321,9 +321,26 @@
// Post-validated implementation of |OrderedIndexSearch|. See
// |OrderedIndexSearch|'s documentation.
- virtual Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const = 0;
+ Range OrderedIndexSearchValidated(FilterOp op,
+ SqlValue value,
+ const OrderedIndices& indices) const;
+
+ // Returns the SqlValue representing the value at a given index.
+ //
+ // This function might be very tempting to use as it appears cheap on the
+ // surface but because of how DataLayerChains might be layered on top of each
+ // other, this might require *several* virtual function calls per index.
+ // If you're tempted to use this, please consider instead create a new
+ // "vectorized" function instead and only using this as a last resort.
+ //
+ // The correct "class" of algorithms to use this function are cases where you
+ // have a set of indices you want to lookup and based on the value returned
+ // you will only use a fraction of them. In this case, it might be worth
+ // paying the non-vectorized lookup to vastly reduce how many indices need
+ // to be translated.
+ //
+ // An example of such an algorithm is binary search on indices.
+ virtual SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const = 0;
};
} // namespace perfetto::trace_processor::column
diff --git a/src/trace_processor/db/column/dense_null_overlay.cc b/src/trace_processor/db/column/dense_null_overlay.cc
index 79ca3c0..426c6d2 100644
--- a/src/trace_processor/db/column/dense_null_overlay.cc
+++ b/src/trace_processor/db/column/dense_null_overlay.cc
@@ -219,50 +219,6 @@
inner_->IndexSearchValidated(op, sql_val, indices);
}
-Range DenseNullOverlay::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- // For NOT EQUAL the further analysis needs to be done by the caller.
- PERFETTO_CHECK(op != FilterOp::kNe);
-
- PERFETTO_TP_TRACE(metatrace::Category::DB,
- "DenseNullOverlay::ChainImpl::OrderedIndexSearch");
-
- // We assume all NULLs are ordered to be in the front. We are looking for the
- // first index that points to non NULL value.
- const uint32_t* first_non_null =
- std::partition_point(indices.data, indices.data + indices.size,
- [this](uint32_t i) { return !non_null_->IsSet(i); });
-
- auto non_null_offset =
- static_cast<uint32_t>(std::distance(indices.data, first_non_null));
- auto non_null_size = static_cast<uint32_t>(
- std::distance(first_non_null, indices.data + indices.size));
-
- if (op == FilterOp::kIsNull) {
- return {0, non_null_offset};
- }
-
- if (op == FilterOp::kIsNotNull) {
- switch (inner_->ValidateSearchConstraints(op, sql_val)) {
- case SearchValidationResult::kNoData:
- return {};
- case SearchValidationResult::kAllData:
- return {non_null_offset, indices.size};
- case SearchValidationResult::kOk:
- break;
- }
- }
-
- Range inner_range = inner_->OrderedIndexSearchValidated(
- op, sql_val,
- OrderedIndices{first_non_null, non_null_size,
- Indices::State::kNonmonotonic});
- return {inner_range.start + non_null_offset,
- inner_range.end + non_null_offset};
-}
-
void DenseNullOverlay::ChainImpl::StableSort(SortToken* start,
SortToken* end,
SortDirection direction) const {
@@ -314,6 +270,12 @@
: *first_null_it;
}
+SqlValue DenseNullOverlay::ChainImpl::Get_AvoidUsingBecauseSlow(
+ uint32_t index) const {
+ return non_null_->IsSet(index) ? inner_->Get_AvoidUsingBecauseSlow(index)
+ : SqlValue();
+}
+
void DenseNullOverlay::ChainImpl::Serialize(StorageProto* storage) const {
auto* null_overlay = storage->set_dense_null_overlay();
non_null_->Serialize(null_overlay->set_bit_vector());
diff --git a/src/trace_processor/db/column/dense_null_overlay.h b/src/trace_processor/db/column/dense_null_overlay.h
index e253400..d0a95d2 100644
--- a/src/trace_processor/db/column/dense_null_overlay.h
+++ b/src/trace_processor/db/column/dense_null_overlay.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include "perfetto/trace_processor/basic_types.h"
@@ -56,10 +57,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -70,6 +67,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override { return non_null_->size(); }
diff --git a/src/trace_processor/db/column/dense_null_overlay_unittest.cc b/src/trace_processor/db/column/dense_null_overlay_unittest.cc
index 24b1eee..7cd74f6 100644
--- a/src/trace_processor/db/column/dense_null_overlay_unittest.cc
+++ b/src/trace_processor/db/column/dense_null_overlay_unittest.cc
@@ -129,11 +129,12 @@
}
TEST(DenseNullOverlay, OrderedIndexSearch) {
- auto fake = FakeStorageChain::SearchSubset(6, BitVector({0, 1, 0, 1, 0, 1}));
+ std::vector<uint32_t> numeric_data{0, 1, 0, 1, 0, 1};
+ NumericStorage<uint32_t> numeric(&numeric_data, ColumnType::kUint32, false);
BitVector bv{0, 1, 0, 1, 0, 1};
DenseNullOverlay storage(&bv);
- auto chain = storage.MakeChain(std::move(fake));
+ auto chain = storage.MakeChain(numeric.MakeChain());
std::vector<uint32_t> indices_vec({0, 2, 4, 1, 3, 5});
OrderedIndices indices{indices_vec.data(), 6, Indices::State::kNonmonotonic};
@@ -146,25 +147,25 @@
ASSERT_EQ(res.start, 3u);
ASSERT_EQ(res.end, 6u);
- res = chain->OrderedIndexSearch(FilterOp::kEq, SqlValue::Long(3), indices);
+ res = chain->OrderedIndexSearch(FilterOp::kEq, SqlValue::Long(1), indices);
ASSERT_EQ(res.start, 3u);
ASSERT_EQ(res.end, 6u);
- res = chain->OrderedIndexSearch(FilterOp::kGt, SqlValue::Long(3), indices);
+ res = chain->OrderedIndexSearch(FilterOp::kGt, SqlValue::Long(0), indices);
ASSERT_EQ(res.start, 3u);
ASSERT_EQ(res.end, 6u);
- res = chain->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(3), indices);
+ res = chain->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(1), indices);
ASSERT_EQ(res.start, 3u);
ASSERT_EQ(res.end, 6u);
- res = chain->OrderedIndexSearch(FilterOp::kLt, SqlValue::Long(3), indices);
- ASSERT_EQ(res.start, 3u);
- ASSERT_EQ(res.end, 6u);
+ res = chain->OrderedIndexSearch(FilterOp::kLt, SqlValue::Long(1), indices);
+ ASSERT_EQ(res.start, 0u);
+ ASSERT_EQ(res.end, 3u);
- res = chain->OrderedIndexSearch(FilterOp::kLe, SqlValue::Long(3), indices);
- ASSERT_EQ(res.start, 3u);
- ASSERT_EQ(res.end, 6u);
+ res = chain->OrderedIndexSearch(FilterOp::kLe, SqlValue::Long(0), indices);
+ ASSERT_EQ(res.start, 0u);
+ ASSERT_EQ(res.end, 3u);
}
TEST(DenseNullOverlay, SingleSearch) {
diff --git a/src/trace_processor/db/column/dummy_storage.cc b/src/trace_processor/db/column/dummy_storage.cc
index 631cbb7..ef9cacf 100644
--- a/src/trace_processor/db/column/dummy_storage.cc
+++ b/src/trace_processor/db/column/dummy_storage.cc
@@ -17,6 +17,7 @@
#include "src/trace_processor/db/column/dummy_storage.h"
#include <cstdint>
+#include <optional>
#include "perfetto/base/logging.h"
#include "perfetto/trace_processor/basic_types.h"
@@ -49,13 +50,6 @@
PERFETTO_FATAL("Shouldn't be called");
}
-Range DummyStorage::ChainImpl::OrderedIndexSearchValidated(
- FilterOp,
- SqlValue,
- const OrderedIndices&) const {
- PERFETTO_FATAL("Shouldn't be called");
-}
-
void DummyStorage::ChainImpl::StableSort(SortToken*,
SortToken*,
SortDirection) const {
@@ -78,6 +72,10 @@
PERFETTO_FATAL("Shouldn't be called");
}
+SqlValue DummyStorage::ChainImpl::Get_AvoidUsingBecauseSlow(uint32_t) const {
+ PERFETTO_FATAL("Shouldn't be called");
+}
+
void DummyStorage::ChainImpl::Serialize(StorageProto*) const {
PERFETTO_FATAL("Shouldn't be called");
}
diff --git a/src/trace_processor/db/column/dummy_storage.h b/src/trace_processor/db/column/dummy_storage.h
index ffa0c73..66f2497 100644
--- a/src/trace_processor/db/column/dummy_storage.h
+++ b/src/trace_processor/db/column/dummy_storage.h
@@ -18,6 +18,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include "perfetto/trace_processor/basic_types.h"
@@ -45,10 +46,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -59,6 +56,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override;
diff --git a/src/trace_processor/db/column/fake_storage.cc b/src/trace_processor/db/column/fake_storage.cc
index 5a26961..5015bf3 100644
--- a/src/trace_processor/db/column/fake_storage.cc
+++ b/src/trace_processor/db/column/fake_storage.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstdint>
#include <iterator>
+#include <optional>
#include <utility>
#include "perfetto/base/logging.h"
@@ -112,43 +113,6 @@
PERFETTO_FATAL("For GCC");
}
-Range FakeStorageChain::OrderedIndexSearchValidated(
- FilterOp,
- SqlValue,
- const OrderedIndices& indices) const {
- switch (strategy_) {
- case kAll:
- return {0, indices.size};
- case kNone:
- return {};
- case kRange: {
- // We are looking at intersection of |range_| and |indices_|.
- const uint32_t* first_in_range = std::partition_point(
- indices.data, indices.data + indices.size,
- [this](uint32_t i) { return !range_.Contains(i); });
- const uint32_t* first_outside_range = std::partition_point(
- first_in_range, indices.data + indices.size,
- [this](uint32_t i) { return range_.Contains(i); });
- return {
- static_cast<uint32_t>(std::distance(indices.data, first_in_range)),
- static_cast<uint32_t>(
- std::distance(indices.data, first_outside_range))};
- }
- case kBitVector:
- // We are looking at intersection of |range_| and |bit_vector_|.
- const uint32_t* first_set = std::partition_point(
- indices.data, indices.data + indices.size,
- [this](uint32_t i) { return !bit_vector_.IsSet(i); });
- const uint32_t* first_non_set = std::partition_point(
- first_set, indices.data + indices.size,
- [this](uint32_t i) { return bit_vector_.IsSet(i); });
- return {
- static_cast<uint32_t>(std::distance(indices.data, first_set)),
- static_cast<uint32_t>(std::distance(indices.data, first_non_set))};
- }
- PERFETTO_FATAL("For GCC");
-}
-
void FakeStorageChain::Distinct(Indices&) const {
// Fake storage shouldn't implement Distinct as it's not a binary (this index
// passes or not) operation on a column.
@@ -166,6 +130,10 @@
PERFETTO_FATAL("Not implemented");
}
+SqlValue FakeStorageChain::Get_AvoidUsingBecauseSlow(uint32_t) const {
+ PERFETTO_FATAL("Not implemented");
+}
+
void FakeStorageChain::Serialize(StorageProto*) const {
// FakeStorage doesn't really make sense to serialize.
PERFETTO_FATAL("Not implemented");
diff --git a/src/trace_processor/db/column/fake_storage.h b/src/trace_processor/db/column/fake_storage.h
index 7adf786..980a951 100644
--- a/src/trace_processor/db/column/fake_storage.h
+++ b/src/trace_processor/db/column/fake_storage.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -84,10 +85,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -95,8 +92,11 @@
void Distinct(Indices&) const override;
std::optional<Token> MaxElement(Indices&) const override;
+
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override { return size_; }
diff --git a/src/trace_processor/db/column/fake_storage_unittest.cc b/src/trace_processor/db/column/fake_storage_unittest.cc
index 908b7e0..28c497f 100644
--- a/src/trace_processor/db/column/fake_storage_unittest.cc
+++ b/src/trace_processor/db/column/fake_storage_unittest.cc
@@ -43,7 +43,6 @@
using testing::IsEmpty;
using Indices = DataLayerChain::Indices;
-using OrderedIndices = DataLayerChain::OrderedIndices;
TEST(FakeStorage, ValidateSearchConstraints) {
{
@@ -197,48 +196,6 @@
}
}
-TEST(FakeStorage, OrderedIndexSearchValidated) {
- std::vector<uint32_t> table_idx{4, 3, 2, 1};
- OrderedIndices indices{table_idx.data(), uint32_t(table_idx.size()),
- Indices::State::kNonmonotonic};
- {
- // All passes
- auto fake = FakeStorageChain::SearchAll(5);
- Range ret =
- fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
- EXPECT_EQ(ret, Range(0, 4));
- }
- {
- // None passes
- auto fake = FakeStorageChain::SearchNone(5);
- Range ret =
- fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
- EXPECT_EQ(ret, Range(0, 0));
- }
- {
- // BitVector
- auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 0, 1, 1, 1});
- Range ret =
- fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
- EXPECT_EQ(ret, Range(0, 3));
- }
- {
- // Index vector
- auto fake =
- FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3});
- Range ret =
- fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
- EXPECT_EQ(ret, Range(1, 4));
- }
- {
- // Range
- auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
- Range ret =
- fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
- EXPECT_EQ(ret, Range(1, 4));
- }
-}
-
} // namespace
} // namespace column
} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/db/column/id_storage.cc b/src/trace_processor/db/column/id_storage.cc
index ea1e909..1ebe459 100644
--- a/src/trace_processor/db/column/id_storage.cc
+++ b/src/trace_processor/db/column/id_storage.cc
@@ -246,47 +246,6 @@
PERFETTO_FATAL("FilterOp not matched");
}
-Range IdStorage::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- PERFETTO_DCHECK(op != FilterOp::kNe);
-
- PERFETTO_TP_TRACE(
- metatrace::Category::DB, "IdStorage::ChainImpl::OrderedIndexSearch",
- [indices, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices.size));
- r->AddArg("Op", std::to_string(static_cast<uint32_t>(op)));
- });
-
- // It's a valid filter operation if |sql_val| is a double, although it
- // requires special logic.
- if (sql_val.type == SqlValue::kDouble) {
- switch (utils::CompareIntColumnWithDouble(op, &sql_val)) {
- case SearchValidationResult::kOk:
- break;
- case SearchValidationResult::kAllData:
- return {0, indices.size};
- case SearchValidationResult::kNoData:
- return {};
- }
- }
- auto val = static_cast<uint32_t>(sql_val.AsLong());
-
- // OrderedIndices are monotonic non contiguous values if OrderedIndexSearch
- // was called. Look for the first and last index and find the result of
- // looking for this range in IdStorage.
- Range indices_range(indices.data[0], indices.data[indices.size - 1] + 1);
- Range bin_search_ret = BinarySearchIntrinsic(op, val, indices_range);
-
- const auto* start_ptr = std::lower_bound(
- indices.data, indices.data + indices.size, bin_search_ret.start);
- const auto* end_ptr = std::lower_bound(start_ptr, indices.data + indices.size,
- bin_search_ret.end);
- return {static_cast<uint32_t>(std::distance(indices.data, start_ptr)),
- static_cast<uint32_t>(std::distance(indices.data, end_ptr))};
-}
-
Range IdStorage::ChainImpl::BinarySearchIntrinsic(FilterOp op,
Id val,
Range range) {
@@ -363,6 +322,10 @@
return *tok;
}
+SqlValue IdStorage::ChainImpl::Get_AvoidUsingBecauseSlow(uint32_t index) const {
+ return SqlValue::Long(index);
+}
+
void IdStorage::ChainImpl::Serialize(StorageProto* storage) const {
storage->set_id_storage();
}
diff --git a/src/trace_processor/db/column/id_storage.h b/src/trace_processor/db/column/id_storage.h
index 420568d..c3aeadf 100644
--- a/src/trace_processor/db/column/id_storage.h
+++ b/src/trace_processor/db/column/id_storage.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <limits>
#include <memory>
+#include <optional>
#include <string>
#include "perfetto/trace_processor/basic_types.h"
@@ -55,10 +56,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -69,6 +66,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override {
diff --git a/src/trace_processor/db/column/null_overlay.cc b/src/trace_processor/db/column/null_overlay.cc
index 128d3ff..372d3b0 100644
--- a/src/trace_processor/db/column/null_overlay.cc
+++ b/src/trace_processor/db/column/null_overlay.cc
@@ -256,56 +256,6 @@
inner_->IndexSearchValidated(op, sql_val, indices);
}
-Range NullOverlay::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- // For NOT EQUAL the translation or results from EQUAL needs to be done by the
- // caller.
- PERFETTO_CHECK(op != FilterOp::kNe);
-
- PERFETTO_TP_TRACE(metatrace::Category::DB,
- "NullOverlay::ChainImpl::OrderedIndexSearch");
-
- // We assume all NULLs are ordered to be in the front. We are looking for the
- // first index that points to non NULL value.
- const uint32_t* first_non_null =
- std::partition_point(indices.data, indices.data + indices.size,
- [this](uint32_t i) { return !non_null_->IsSet(i); });
- auto non_null_offset =
- static_cast<uint32_t>(std::distance(indices.data, first_non_null));
- auto non_null_size = static_cast<uint32_t>(
- std::distance(first_non_null, indices.data + indices.size));
-
- if (op == FilterOp::kIsNull) {
- return {0, non_null_offset};
- }
-
- if (op == FilterOp::kIsNotNull) {
- switch (inner_->ValidateSearchConstraints(op, sql_val)) {
- case SearchValidationResult::kNoData:
- return {};
- case SearchValidationResult::kAllData:
- return {non_null_offset, indices.size};
- case SearchValidationResult::kOk:
- break;
- }
- }
-
- std::vector<uint32_t> storage_iv;
- storage_iv.reserve(non_null_size);
- for (const uint32_t* it = first_non_null;
- it != first_non_null + non_null_size; it++) {
- storage_iv.push_back(non_null_->CountSetBits(*it));
- }
-
- Range inner_range = inner_->OrderedIndexSearchValidated(
- op, sql_val,
- OrderedIndices{storage_iv.data(), non_null_size, indices.state});
- return {inner_range.start + non_null_offset,
- inner_range.end + non_null_offset};
-}
-
void NullOverlay::ChainImpl::StableSort(SortToken* start,
SortToken* end,
SortDirection direction) const {
@@ -369,6 +319,13 @@
return inner_->MinElement(indices);
}
+SqlValue NullOverlay::ChainImpl::Get_AvoidUsingBecauseSlow(
+ uint32_t index) const {
+ return non_null_->IsSet(index)
+ ? inner_->Get_AvoidUsingBecauseSlow(non_null_->CountSetBits(index))
+ : SqlValue();
+}
+
void NullOverlay::ChainImpl::Serialize(StorageProto* storage) const {
auto* null_storage = storage->set_null_overlay();
non_null_->Serialize(null_storage->set_bit_vector());
diff --git a/src/trace_processor/db/column/null_overlay.h b/src/trace_processor/db/column/null_overlay.h
index 187c056..a74f211 100644
--- a/src/trace_processor/db/column/null_overlay.h
+++ b/src/trace_processor/db/column/null_overlay.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include "perfetto/trace_processor/basic_types.h"
@@ -55,10 +56,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -69,6 +66,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override { return non_null_->size(); }
diff --git a/src/trace_processor/db/column/null_overlay_unittest.cc b/src/trace_processor/db/column/null_overlay_unittest.cc
index 981c7bd..9cff095 100644
--- a/src/trace_processor/db/column/null_overlay_unittest.cc
+++ b/src/trace_processor/db/column/null_overlay_unittest.cc
@@ -200,15 +200,15 @@
}
TEST(NullOverlay, OrderedIndexSearch) {
+ std::vector<uint32_t> numeric_data{1, 0, 1, 0};
+ NumericStorage<uint32_t> numeric(&numeric_data, ColumnType::kUint32, false);
+
BitVector bv{0, 1, 1, 1, 0, 1};
- // Passing values in final storage (on normal operations)
- // 0, 1, 0, 1, 0, 0
- auto fake = FakeStorageChain::SearchSubset(4, BitVector{1, 0, 1, 0});
NullOverlay storage(&bv);
- auto chain = storage.MakeChain(std::move(fake));
+ auto chain = storage.MakeChain(numeric.MakeChain());
// Passing values on final data
- // NULL, NULL, 0, 1, 1
+ // NULL, NULL, 0, 0, 1, 1
std::vector<uint32_t> table_idx{0, 4, 5, 1, 3};
OrderedIndices indices{table_idx.data(), uint32_t(table_idx.size()),
Indices::State::kNonmonotonic};
@@ -218,28 +218,28 @@
ASSERT_EQ(res.end, 2u);
res = chain->OrderedIndexSearch(FilterOp::kIsNotNull, SqlValue(), indices);
+ ASSERT_EQ(res.start, 2u);
+ ASSERT_EQ(res.end, 5u);
+
+ res = chain->OrderedIndexSearch(FilterOp::kEq, SqlValue::Long(1), indices);
ASSERT_EQ(res.start, 3u);
ASSERT_EQ(res.end, 5u);
- res = chain->OrderedIndexSearch(FilterOp::kEq, SqlValue::Long(3), indices);
+ res = chain->OrderedIndexSearch(FilterOp::kGt, SqlValue::Long(0), indices);
ASSERT_EQ(res.start, 3u);
ASSERT_EQ(res.end, 5u);
- res = chain->OrderedIndexSearch(FilterOp::kGt, SqlValue::Long(3), indices);
+ res = chain->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(1), indices);
ASSERT_EQ(res.start, 3u);
ASSERT_EQ(res.end, 5u);
- res = chain->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(3), indices);
- ASSERT_EQ(res.start, 3u);
- ASSERT_EQ(res.end, 5u);
+ res = chain->OrderedIndexSearch(FilterOp::kLt, SqlValue::Long(1), indices);
+ ASSERT_EQ(res.start, 0u);
+ ASSERT_EQ(res.end, 3u);
- res = chain->OrderedIndexSearch(FilterOp::kLt, SqlValue::Long(3), indices);
- ASSERT_EQ(res.start, 3u);
- ASSERT_EQ(res.end, 5u);
-
- res = chain->OrderedIndexSearch(FilterOp::kLe, SqlValue::Long(3), indices);
- ASSERT_EQ(res.start, 3u);
- ASSERT_EQ(res.end, 5u);
+ res = chain->OrderedIndexSearch(FilterOp::kLe, SqlValue::Long(0), indices);
+ ASSERT_EQ(res.start, 0u);
+ ASSERT_EQ(res.end, 3u);
}
TEST(NullOverlay, StableSort) {
diff --git a/src/trace_processor/db/column/numeric_storage.cc b/src/trace_processor/db/column/numeric_storage.cc
index b39b4cb..3612356 100644
--- a/src/trace_processor/db/column/numeric_storage.cc
+++ b/src/trace_processor/db/column/numeric_storage.cc
@@ -138,60 +138,6 @@
}
template <typename T>
-uint32_t TypedLowerBoundExtrinsic(T val,
- const T* data,
- OrderedIndices indices) {
- const auto* lower = std::lower_bound(
- indices.data, indices.data + indices.size, val,
- [data](uint32_t index, T value) { return data[index] < value; });
- return static_cast<uint32_t>(std::distance(indices.data, lower));
-}
-
-uint32_t LowerBoundExtrinsic(const void* vector_ptr,
- NumericValue val,
- OrderedIndices indices) {
- if (const auto* u32 = std::get_if<uint32_t>(&val)) {
- const auto* start =
- static_cast<const std::vector<uint32_t>*>(vector_ptr)->data();
- return TypedLowerBoundExtrinsic(*u32, start, indices);
- }
- if (const auto* i64 = std::get_if<int64_t>(&val)) {
- const auto* start =
- static_cast<const std::vector<int64_t>*>(vector_ptr)->data();
- return TypedLowerBoundExtrinsic(*i64, start, indices);
- }
- if (const auto* i32 = std::get_if<int32_t>(&val)) {
- const auto* start =
- static_cast<const std::vector<int32_t>*>(vector_ptr)->data();
- return TypedLowerBoundExtrinsic(*i32, start, indices);
- }
- if (const auto* db = std::get_if<double>(&val)) {
- const auto* start =
- static_cast<const std::vector<double>*>(vector_ptr)->data();
- return TypedLowerBoundExtrinsic(*db, start, indices);
- }
- PERFETTO_FATAL("Type not handled");
-}
-
-uint32_t UpperBoundExtrinsic(const void* vector_ptr,
- NumericValue val,
- OrderedIndices indices) {
- return std::visit(
- [vector_ptr, indices](auto val_data) {
- using T = decltype(val_data);
- const T* typed_start =
- static_cast<const std::vector<T>*>(vector_ptr)->data();
- const auto* upper =
- std::upper_bound(indices.data, indices.data + indices.size,
- val_data, [typed_start](T value, uint32_t index) {
- return value < typed_start[index];
- });
- return static_cast<uint32_t>(std::distance(indices.data, upper));
- },
- val);
-}
-
-template <typename T>
void TypedLinearSearch(T typed_val,
const T* start,
FilterOp op,
@@ -504,79 +450,6 @@
val);
}
-Range NumericStorageBase::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- PERFETTO_TP_TRACE(
- metatrace::Category::DB, "NumericStorage::ChainImpl::OrderedIndexSearch",
- [indices, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices.size));
- r->AddArg("Op", std::to_string(static_cast<uint32_t>(op)));
- });
-
- // Mismatched types - value is double and column is int.
- if (sql_val.type == SqlValue::kDouble &&
- storage_type_ != ColumnType::kDouble) {
- if (auto ret = utils::CanReturnEarly(IntColumnWithDouble(op, &sql_val),
- indices.size);
- ret) {
- return *ret;
- }
- }
-
- // Mismatched types - column is double and value is int.
- if (sql_val.type != SqlValue::kDouble &&
- storage_type_ == ColumnType::kDouble) {
- if (auto ret = utils::CanReturnEarly(DoubleColumnWithInt(op, &sql_val),
- indices.size);
- ret) {
- return *ret;
- }
- }
-
- NumericValue val;
- switch (storage_type_) {
- case ColumnType::kDouble:
- val = sql_val.AsDouble();
- break;
- case ColumnType::kInt64:
- val = sql_val.AsLong();
- break;
- case ColumnType::kInt32:
- val = static_cast<int32_t>(sql_val.AsLong());
- break;
- case ColumnType::kUint32:
- val = static_cast<uint32_t>(sql_val.AsLong());
- break;
- case ColumnType::kString:
- case ColumnType::kDummy:
- case ColumnType::kId:
- PERFETTO_FATAL("Invalid type");
- }
-
- switch (op) {
- case FilterOp::kEq:
- return {LowerBoundExtrinsic(vector_ptr_, val, indices),
- UpperBoundExtrinsic(vector_ptr_, val, indices)};
- case FilterOp::kLe:
- return {0, UpperBoundExtrinsic(vector_ptr_, val, indices)};
- case FilterOp::kLt:
- return {0, LowerBoundExtrinsic(vector_ptr_, val, indices)};
- case FilterOp::kGe:
- return {LowerBoundExtrinsic(vector_ptr_, val, indices), indices.size};
- case FilterOp::kGt:
- return {UpperBoundExtrinsic(vector_ptr_, val, indices), indices.size};
- case FilterOp::kNe:
- case FilterOp::kIsNull:
- case FilterOp::kIsNotNull:
- case FilterOp::kGlob:
- case FilterOp::kRegex:
- PERFETTO_FATAL("Wrong filtering operation");
- }
- PERFETTO_FATAL("For GCC");
-}
-
BitVector NumericStorageBase::ChainImpl::LinearSearchInternal(
FilterOp op,
NumericValue val,
diff --git a/src/trace_processor/db/column/numeric_storage.h b/src/trace_processor/db/column/numeric_storage.h
index fd27d26..3c4416b 100644
--- a/src/trace_processor/db/column/numeric_storage.h
+++ b/src/trace_processor/db/column/numeric_storage.h
@@ -23,6 +23,7 @@
#include <optional>
#include <set>
#include <string>
+#include <type_traits>
#include <unordered_set>
#include <variant>
#include <vector>
@@ -49,10 +50,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void Serialize(StorageProto*) const override;
std::string DebugString() const override { return "NumericStorage"; }
@@ -145,6 +142,13 @@
return *tok;
}
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override {
+ if constexpr (std::is_same_v<T, double>) {
+ return SqlValue::Double((*vector_)[index]);
+ }
+ return SqlValue::Long((*vector_)[index]);
+ }
+
void StableSort(SortToken* start,
SortToken* end,
SortDirection direction) const override {
diff --git a/src/trace_processor/db/column/range_overlay.cc b/src/trace_processor/db/column/range_overlay.cc
index 81bb7ae..dee7838 100644
--- a/src/trace_processor/db/column/range_overlay.cc
+++ b/src/trace_processor/db/column/range_overlay.cc
@@ -18,6 +18,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <utility>
#include <vector>
@@ -118,22 +119,6 @@
inner_->IndexSearchValidated(op, sql_val, indices);
}
-Range RangeOverlay::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- PERFETTO_TP_TRACE(metatrace::Category::DB, "RangeOverlay::IndexSearch");
-
- // Should be SIMD optimized.
- std::vector<uint32_t> storage_iv(indices.size);
- for (uint32_t i = 0; i < indices.size; ++i) {
- storage_iv[i] = indices.data[i] + range_->start;
- }
- return inner_->OrderedIndexSearchValidated(
- op, sql_val,
- OrderedIndices{storage_iv.data(), indices.size, indices.state});
-}
-
void RangeOverlay::ChainImpl::StableSort(SortToken* start,
SortToken* end,
SortDirection direction) const {
@@ -160,6 +145,11 @@
return inner_->MaxElement(indices);
}
+SqlValue RangeOverlay::ChainImpl::Get_AvoidUsingBecauseSlow(
+ uint32_t index) const {
+ return inner_->Get_AvoidUsingBecauseSlow(index + range_->start);
+}
+
std::optional<Token> RangeOverlay::ChainImpl::MinElement(
Indices& indices) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "RangeOverlay::MinElement");
diff --git a/src/trace_processor/db/column/range_overlay.h b/src/trace_processor/db/column/range_overlay.h
index f15e8dc..a58df40 100644
--- a/src/trace_processor/db/column/range_overlay.h
+++ b/src/trace_processor/db/column/range_overlay.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include "perfetto/trace_processor/basic_types.h"
@@ -52,10 +53,6 @@
void IndexSearchValidated(FilterOp p, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -66,6 +63,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override { return range_->size(); }
diff --git a/src/trace_processor/db/column/selector_overlay.cc b/src/trace_processor/db/column/selector_overlay.cc
index b2cbc43..9e957cb 100644
--- a/src/trace_processor/db/column/selector_overlay.cc
+++ b/src/trace_processor/db/column/selector_overlay.cc
@@ -16,9 +16,9 @@
#include "src/trace_processor/db/column/selector_overlay.h"
-#include <algorithm>
#include <cstdint>
#include <memory>
+#include <optional>
#include <utility>
#include <vector>
@@ -35,7 +35,7 @@
namespace perfetto::trace_processor::column {
namespace {
-static constexpr uint32_t kIndexOfNthSetRatio = 32;
+constexpr uint32_t kIndexOfNthSetRatio = 32;
} // namespace
@@ -97,23 +97,6 @@
return inner_->IndexSearchValidated(op, sql_val, indices);
}
-Range SelectorOverlay::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- // To go from TableIndexVector to StorageIndexVector we need to find index in
- // |selector_| by looking only into set bits.
- std::vector<uint32_t> inner_indices(indices.size);
- for (uint32_t i = 0; i < indices.size; ++i) {
- inner_indices[i] = selector_->IndexOfNthSet(indices.data[i]);
- }
- return inner_->OrderedIndexSearchValidated(
- op, sql_val,
- OrderedIndices{inner_indices.data(),
- static_cast<uint32_t>(inner_indices.size()),
- indices.state});
-}
-
void SelectorOverlay::ChainImpl::StableSort(SortToken* start,
SortToken* end,
SortDirection direction) const {
@@ -148,6 +131,11 @@
return inner_->MinElement(indices);
}
+SqlValue SelectorOverlay::ChainImpl::Get_AvoidUsingBecauseSlow(
+ uint32_t index) const {
+ return inner_->Get_AvoidUsingBecauseSlow(selector_->IndexOfNthSet(index));
+}
+
void SelectorOverlay::ChainImpl::Serialize(StorageProto* storage) const {
auto* selector_overlay = storage->set_selector_overlay();
inner_->Serialize(selector_overlay->set_storage());
@@ -164,15 +152,15 @@
for (auto& token : indices.tokens) {
token.index = selector_->IndexOfNthSet(token.index);
}
- } else {
- // TODO(mayzner): once we have a reverse index for IndexOfNthSet in
- // BitVector, this should no longer be necessary.
- std::vector<uint32_t> lookup = selector_->GetSetBitIndices();
- for (auto& token : indices.tokens) {
- token.index = lookup[token.index];
- }
+ return;
}
- return;
+
+ // TODO(mayzner): once we have a reverse index for IndexOfNthSet in
+ // BitVector, this should no longer be necessary.
+ std::vector<uint32_t> lookup = selector_->GetSetBitIndices();
+ for (auto& token : indices.tokens) {
+ token.index = lookup[token.index];
+ }
}
} // namespace perfetto::trace_processor::column
diff --git a/src/trace_processor/db/column/selector_overlay.h b/src/trace_processor/db/column/selector_overlay.h
index d54bdf5..3726f9f 100644
--- a/src/trace_processor/db/column/selector_overlay.h
+++ b/src/trace_processor/db/column/selector_overlay.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include "perfetto/trace_processor/basic_types.h"
@@ -56,10 +57,6 @@
void IndexSearchValidated(FilterOp p, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection) const override;
@@ -70,6 +67,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override { return selector_->size(); }
diff --git a/src/trace_processor/db/column/selector_overlay_unittest.cc b/src/trace_processor/db/column/selector_overlay_unittest.cc
index 0abae4c..def2a77 100644
--- a/src/trace_processor/db/column/selector_overlay_unittest.cc
+++ b/src/trace_processor/db/column/selector_overlay_unittest.cc
@@ -17,6 +17,7 @@
#include "src/trace_processor/db/column/selector_overlay.h"
#include <cstdint>
+#include <utility>
#include <vector>
#include "data_layer.h"
@@ -103,35 +104,23 @@
ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(1u));
}
-TEST(SelectorOverlay, OrderedIndexSearchTrivial) {
+TEST(SelectorOverlay, OrderedIndexSearch) {
+ std::vector<uint32_t> numeric_data{1, 0, 0, 1, 1};
+ NumericStorage<uint32_t> numeric(&numeric_data, ColumnType::kUint32, false);
+
BitVector selector{1, 0, 1, 0, 1};
- auto fake = FakeStorageChain::SearchAll(5);
SelectorOverlay storage(&selector);
- auto chain = storage.MakeChain(std::move(fake));
+ auto chain = storage.MakeChain(numeric.MakeChain());
std::vector<uint32_t> table_idx{1u, 0u, 2u};
Range res = chain->OrderedIndexSearch(
- FilterOp::kGe, SqlValue::Long(0u),
+ FilterOp::kGe, SqlValue::Long(1u),
OrderedIndices{table_idx.data(), static_cast<uint32_t>(table_idx.size()),
Indices::State::kNonmonotonic});
- ASSERT_EQ(res.start, 0u);
+ ASSERT_EQ(res.start, 1u);
ASSERT_EQ(res.end, 3u);
}
-TEST(SelectorOverlay, OrderedIndexSearchNone) {
- BitVector selector{1, 0, 1, 0, 1};
- auto fake = FakeStorageChain::SearchNone(5);
- SelectorOverlay storage(&selector);
- auto chain = storage.MakeChain(std::move(fake));
-
- std::vector<uint32_t> table_idx{1u, 0u, 2u};
- Range res = chain->OrderedIndexSearch(
- FilterOp::kGe, SqlValue::Long(0u),
- OrderedIndices{table_idx.data(), static_cast<uint32_t>(table_idx.size()),
- Indices::State::kNonmonotonic});
- ASSERT_EQ(res.size(), 0u);
-}
-
TEST(SelectorOverlay, StableSort) {
std::vector<uint32_t> numeric_data{3, 1, 0, 0, 2, 4, 3, 4};
NumericStorage<uint32_t> numeric(&numeric_data, ColumnType::kUint32, false);
diff --git a/src/trace_processor/db/column/set_id_storage.cc b/src/trace_processor/db/column/set_id_storage.cc
index 207b649..42ab4ea 100644
--- a/src/trace_processor/db/column/set_id_storage.cc
+++ b/src/trace_processor/db/column/set_id_storage.cc
@@ -19,10 +19,7 @@
#include <algorithm>
#include <cstdint>
#include <functional>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <set>
+#include <optional>
#include <string>
#include <unordered_set>
#include <utility>
@@ -250,38 +247,17 @@
}
}
-Range SetIdStorage::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- PERFETTO_TP_TRACE(metatrace::Category::DB,
- "SetIdStorage::ChainImpl::OrderedIndexSearch");
- // OrderedIndices are monotonic non-contiguous values.
- auto res = SearchValidated(
- op, sql_val, Range(indices.data[0], indices.data[indices.size - 1] + 1));
- PERFETTO_CHECK(res.IsRange());
- Range res_range = std::move(res).TakeIfRange();
-
- const auto* start_ptr = std::lower_bound(
- indices.data, indices.data + indices.size, res_range.start);
- const auto* end_ptr =
- std::lower_bound(start_ptr, indices.data + indices.size, res_range.end);
-
- return {static_cast<uint32_t>(std::distance(indices.data, start_ptr)),
- static_cast<uint32_t>(std::distance(indices.data, end_ptr))};
-}
-
Range SetIdStorage::ChainImpl::BinarySearchIntrinsic(FilterOp op,
SetId val,
Range range) const {
switch (op) {
case FilterOp::kEq: {
- if (values_->data()[val] != val) {
- return Range();
+ if ((*values_)[val] != val) {
+ return {};
}
uint32_t start = std::max(val, range.start);
uint32_t end = UpperBoundIntrinsic(values_->data(), val, range);
- return Range(std::min(start, end), end);
+ return {std::min(start, end), end};
}
case FilterOp::kLe: {
return {range.start, UpperBoundIntrinsic(values_->data(), val, range)};
@@ -370,6 +346,11 @@
return *tok;
}
+SqlValue SetIdStorage::ChainImpl::Get_AvoidUsingBecauseSlow(
+ uint32_t index) const {
+ return SqlValue::Long((*values_)[index]);
+}
+
void SetIdStorage::ChainImpl::Serialize(StorageProto* msg) const {
auto* vec_msg = msg->set_set_id_storage();
vec_msg->set_values(reinterpret_cast<const uint8_t*>(values_->data()),
diff --git a/src/trace_processor/db/column/set_id_storage.h b/src/trace_processor/db/column/set_id_storage.h
index 2fa6c81..e459106 100644
--- a/src/trace_processor/db/column/set_id_storage.h
+++ b/src/trace_processor/db/column/set_id_storage.h
@@ -18,6 +18,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -54,10 +55,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection direction) const override;
@@ -70,6 +67,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
uint32_t size() const override {
return static_cast<uint32_t>(values_->size());
}
diff --git a/src/trace_processor/db/column/string_storage.cc b/src/trace_processor/db/column/string_storage.cc
index adf3eda..1d25dbb 100644
--- a/src/trace_processor/db/column/string_storage.cc
+++ b/src/trace_processor/db/column/string_storage.cc
@@ -163,36 +163,6 @@
return static_cast<uint32_t>(std::distance(data, upper));
}
-uint32_t LowerBoundExtrinsic(StringPool* pool,
- const StringPool::Id* data,
- NullTermStringView val,
- const uint32_t* indices,
- uint32_t indices_count,
- uint32_t offset) {
- Less comp{pool};
- const auto* lower =
- std::lower_bound(indices + offset, indices + indices_count, val,
- [comp, data](uint32_t index, NullTermStringView val) {
- return comp(data[index], val);
- });
- return static_cast<uint32_t>(std::distance(indices, lower));
-}
-
-uint32_t UpperBoundExtrinsic(StringPool* pool,
- const StringPool::Id* data,
- NullTermStringView val,
- const uint32_t* indices,
- uint32_t indices_count,
- uint32_t offset) {
- Greater comp{pool};
- const auto* upper =
- std::upper_bound(indices + offset, indices + indices_count, val,
- [comp, data](NullTermStringView val, uint32_t index) {
- return comp(data[index], val);
- });
- return static_cast<uint32_t>(std::distance(indices, upper));
-}
-
} // namespace
StringStorage::ChainImpl::ChainImpl(StringPool* string_pool,
@@ -518,64 +488,6 @@
return std::move(builder).Build();
}
-Range StringStorage::ChainImpl::OrderedIndexSearchValidated(
- FilterOp op,
- SqlValue sql_val,
- const OrderedIndices& indices) const {
- PERFETTO_TP_TRACE(metatrace::Category::DB,
- "StringStorage::ChainImpl::OrderedIndexSearch");
- StringPool::Id val =
- (op == FilterOp::kIsNull || op == FilterOp::kIsNotNull)
- ? StringPool::Id::Null()
- : string_pool_->InternString(base::StringView(sql_val.AsString()));
- NullTermStringView val_str = string_pool_->Get(val);
-
- auto first_non_null = static_cast<uint32_t>(std::distance(
- indices.data,
- std::partition_point(indices.data, indices.data + indices.size,
- [this](uint32_t i) {
- return (*data_)[i] == StringPool::Id::Null();
- })));
-
- switch (op) {
- case FilterOp::kEq:
- return {LowerBoundExtrinsic(string_pool_, data_->data(), val_str,
- indices.data, indices.size, first_non_null),
- UpperBoundExtrinsic(string_pool_, data_->data(), val_str,
- indices.data, indices.size, first_non_null)};
- case FilterOp::kLe:
- return {first_non_null,
- UpperBoundExtrinsic(string_pool_, data_->data(), val_str,
- indices.data, indices.size, first_non_null)};
- case FilterOp::kLt:
- return {first_non_null,
- LowerBoundExtrinsic(string_pool_, data_->data(), val_str,
- indices.data, indices.size, first_non_null)};
- case FilterOp::kGe:
- return {LowerBoundExtrinsic(string_pool_, data_->data(), val_str,
- indices.data, indices.size, first_non_null),
- indices.size};
- case FilterOp::kGt:
- return {UpperBoundExtrinsic(string_pool_, data_->data(), val_str,
- indices.data, indices.size, first_non_null),
- indices.size};
- case FilterOp::kIsNull: {
- // Assuming nulls are at the front.
- return Range(0, first_non_null);
- }
- case FilterOp::kIsNotNull: {
- // Assuming nulls are at the front.
- return Range(first_non_null, indices.size);
- }
-
- case FilterOp::kNe:
- case FilterOp::kGlob:
- case FilterOp::kRegex:
- PERFETTO_FATAL("Not supported for OrderedIndexSearch");
- }
- PERFETTO_FATAL("For GCC");
-}
-
Range StringStorage::ChainImpl::BinarySearchIntrinsic(
FilterOp op,
SqlValue sql_val,
@@ -715,6 +627,14 @@
return *tok;
}
+SqlValue StringStorage::ChainImpl::Get_AvoidUsingBecauseSlow(
+ uint32_t index) const {
+ StringPool::Id id = (*data_)[index];
+ return id == StringPool::Id::Null()
+ ? SqlValue()
+ : SqlValue::String(string_pool_->Get(id).c_str());
+}
+
void StringStorage::ChainImpl::Serialize(StorageProto* msg) const {
auto* string_storage_msg = msg->set_string_storage();
string_storage_msg->set_is_sorted(is_sorted_);
diff --git a/src/trace_processor/db/column/string_storage.h b/src/trace_processor/db/column/string_storage.h
index 470ccd1..1c05dd7 100644
--- a/src/trace_processor/db/column/string_storage.h
+++ b/src/trace_processor/db/column/string_storage.h
@@ -18,6 +18,7 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -57,10 +58,6 @@
void IndexSearchValidated(FilterOp, SqlValue, Indices&) const override;
- Range OrderedIndexSearchValidated(FilterOp,
- SqlValue,
- const OrderedIndices&) const override;
-
void StableSort(SortToken* start,
SortToken* end,
SortDirection direction) const override;
@@ -71,6 +68,8 @@
std::optional<Token> MinElement(Indices&) const override;
+ SqlValue Get_AvoidUsingBecauseSlow(uint32_t index) const override;
+
void Serialize(StorageProto*) const override;
uint32_t size() const override {
diff --git a/src/trace_processor/db/column/string_storage_unittest.cc b/src/trace_processor/db/column/string_storage_unittest.cc
index 4474ea9..7e7cadb 100644
--- a/src/trace_processor/db/column/string_storage_unittest.cc
+++ b/src/trace_processor/db/column/string_storage_unittest.cc
@@ -383,12 +383,12 @@
op = FilterOp::kLt;
res = chain->OrderedIndexSearch(op, val, indices);
- ASSERT_EQ(res.start, 1u);
+ ASSERT_EQ(res.start, 0u);
ASSERT_EQ(res.end, 4u);
op = FilterOp::kLe;
res = chain->OrderedIndexSearch(op, val, indices);
- ASSERT_EQ(res.start, 1u);
+ ASSERT_EQ(res.start, 0u);
ASSERT_EQ(res.end, 5u);
op = FilterOp::kGt;
@@ -402,6 +402,7 @@
ASSERT_EQ(res.end, 6u);
op = FilterOp::kIsNull;
+ val = SqlValue();
res = chain->OrderedIndexSearch(op, val, indices);
ASSERT_EQ(res.start, 0u);
ASSERT_EQ(res.end, 1u);
diff --git a/src/trace_processor/db/query_executor_benchmark.cc b/src/trace_processor/db/query_executor_benchmark.cc
index d6cc57f..dcd75f0 100644
--- a/src/trace_processor/db/query_executor_benchmark.cc
+++ b/src/trace_processor/db/query_executor_benchmark.cc
@@ -513,6 +513,29 @@
}
BENCHMARK(BM_QEFilterOrderedArrangement);
+void BM_QEFilterNullOrderedArrangement(benchmark::State& state) {
+ SliceTableForBenchmark table(state);
+ Order order{table.table_.parent_id().index_in_table(), false};
+ Table slice_sorted_with_parent_id = table.table_.Sort({order});
+
+ Constraint c{table.table_.parent_id().index_in_table(), FilterOp::kGt,
+ SqlValue::Long(26091)};
+ Query q;
+ q.constraints = {c};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(slice_sorted_with_parent_id.QueryToRowMap(q));
+ }
+ state.counters["s/row"] = benchmark::Counter(
+ static_cast<double>(slice_sorted_with_parent_id.row_count()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
+ state.counters["s/out"] = benchmark::Counter(
+ static_cast<double>(table.table_.QueryToRowMap(q).size()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
+}
+BENCHMARK(BM_QEFilterNullOrderedArrangement);
+
void BM_QESliceFilterIndexSearchOneElement(benchmark::State& state) {
SliceTableForBenchmark table(state);
BenchmarkSliceTableFilter(
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index 369917d..a3b6a7f 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -29,6 +29,7 @@
#include "src/trace_processor/trace_reader_registry.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/trace_type.h"
namespace perfetto {
namespace trace_processor {
@@ -53,7 +54,7 @@
case kSystraceTraceType:
case kGzipTraceType:
case kCtraceTraceType:
- case kAndroidBugreportTraceType:
+ case kZipFile:
return std::nullopt;
case kPerfDataTraceType:
@@ -102,26 +103,11 @@
reader_ = std::move(*reader_or);
PERFETTO_DLOG("%s detected", ToString(trace_type));
- std::optional<TraceSorter::SortingMode> minimum_sorting_mode =
- GetMinimumSortingMode(trace_type, *context_);
+ UpdateSorterForTraceType(trace_type);
- if (minimum_sorting_mode.has_value()) {
- if (!context_->sorter) {
- context_->sorter.reset(new TraceSorter(context_, *minimum_sorting_mode));
- }
-
- switch (context_->sorter->sorting_mode()) {
- case TraceSorter::SortingMode::kDefault:
- PERFETTO_CHECK(minimum_sorting_mode ==
- TraceSorter::SortingMode::kDefault);
- break;
- case TraceSorter::SortingMode::kFullSort:
- break;
- }
- }
-
- // TODO(carlscab) Make sure kProtoTraceType and kSystraceTraceType are parsed
- // first so that we do not get issues with SetPidZeroIsUpidZeroIdleProcess()
+ // TODO(b/334978369) Make sure kProtoTraceType and kSystraceTraceType are
+ // parsed first so that we do not get issues with
+ // SetPidZeroIsUpidZeroIdleProcess()
if (trace_type == kProtoTraceType || trace_type == kSystraceTraceType) {
context_->process_tracker->SetPidZeroIsUpidZeroIdleProcess();
}
@@ -129,6 +115,27 @@
return base::OkStatus();
}
+void ForwardingTraceParser::UpdateSorterForTraceType(TraceType trace_type) {
+ std::optional<TraceSorter::SortingMode> minimum_sorting_mode =
+ GetMinimumSortingMode(trace_type, *context_);
+ if (!minimum_sorting_mode.has_value()) {
+ return;
+ }
+
+ if (!context_->sorter) {
+ context_->sorter.reset(new TraceSorter(context_, *minimum_sorting_mode));
+ }
+
+ switch (context_->sorter->sorting_mode()) {
+ case TraceSorter::SortingMode::kDefault:
+ PERFETTO_CHECK(minimum_sorting_mode ==
+ TraceSorter::SortingMode::kDefault);
+ break;
+ case TraceSorter::SortingMode::kFullSort:
+ break;
+ }
+}
+
base::Status ForwardingTraceParser::Parse(TraceBlobView blob) {
// If this is the first Parse() call, guess the trace type and create the
// appropriate parser.
diff --git a/src/trace_processor/forwarding_trace_parser.h b/src/trace_processor/forwarding_trace_parser.h
index 1f32938..12fe447 100644
--- a/src/trace_processor/forwarding_trace_parser.h
+++ b/src/trace_processor/forwarding_trace_parser.h
@@ -20,6 +20,7 @@
#include "perfetto/base/status.h"
#include "perfetto/trace_processor/trace_blob_view.h"
#include "src/trace_processor/importers/common/chunked_trace_reader.h"
+#include "src/trace_processor/util/trace_type.h"
namespace perfetto {
namespace trace_processor {
@@ -37,6 +38,7 @@
private:
base::Status Init(const TraceBlobView&);
+ void UpdateSorterForTraceType(TraceType trace_type);
TraceProcessorContext* const context_;
std::unique_ptr<ChunkedTraceReader> reader_;
};
diff --git a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
index 87aa0f5..1d46efc 100644
--- a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
+++ b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
@@ -17,54 +17,90 @@
#include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h"
#include <algorithm>
+#include <cstddef>
#include <optional>
+#include <string>
+#include <vector>
#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
#include "perfetto/ext/base/string_utils.h"
-#include "perfetto/trace_processor/trace_blob.h"
-#include "perfetto/trace_processor/trace_blob_view.h"
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
#include "src/trace_processor/importers/android_bugreport/android_log_parser.h"
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "src/trace_processor/util/zip_reader.h"
-#include "protos/perfetto/common/builtin_clock.pbzero.h"
-
namespace perfetto {
namespace trace_processor {
+namespace {
+const util::ZipFile* FindBugReportFile(
+ const std::vector<util::ZipFile>& zip_file_entries) {
+ for (const auto& zf : zip_file_entries) {
+ if (base::StartsWith(zf.name(), "bugreport-") &&
+ base::EndsWith(zf.name(), ".txt")) {
+ return &zf;
+ }
+ }
+ return nullptr;
+}
-AndroidBugreportParser::AndroidBugreportParser(TraceProcessorContext* ctx)
- : context_(ctx), zip_reader_(new util::ZipReader()) {}
+std::optional<int32_t> ExtractYearFromBugReportFilename(
+ const std::string& filename) {
+ // Typical name: "bugreport-product-TP1A.220623.001-2022-06-24-16-24-37.txt".
+ auto year_str =
+ filename.substr(filename.size() - strlen("2022-12-31-23-59-00.txt"), 4);
+ return base::StringToInt32(year_str);
+}
+
+} // namespace
+
+// static
+bool AndroidBugreportParser::IsAndroidBugReport(
+ const std::vector<util::ZipFile>& zip_file_entries) {
+ if (const util::ZipFile* file = FindBugReportFile(zip_file_entries);
+ file != nullptr) {
+ return ExtractYearFromBugReportFilename(file->name()).has_value();
+ }
+
+ return false;
+}
+
+// static
+util::Status AndroidBugreportParser::Parse(
+ TraceProcessorContext* context,
+ std::vector<util::ZipFile> zip_file_entries) {
+ return AndroidBugreportParser(context, std::move(zip_file_entries))
+ .ParseImpl();
+}
+
+AndroidBugreportParser::AndroidBugreportParser(
+ TraceProcessorContext* context,
+ std::vector<util::ZipFile> zip_file_entries)
+ : context_(context), zip_file_entries_(std::move(zip_file_entries)) {}
AndroidBugreportParser::~AndroidBugreportParser() = default;
-util::Status AndroidBugreportParser::Parse(TraceBlobView tbv) {
- if (!first_chunk_seen_) {
- first_chunk_seen_ = true;
- // All logs in Android bugreports use wall time (which creates problems
- // in case of early boot events before NTP kicks in, which get emitted as
- // 1970), but that is the state of affairs.
- context_->clock_tracker->SetTraceTimeClock(
- protos::pbzero::BUILTIN_CLOCK_REALTIME);
- }
-
- return zip_reader_->Parse(tbv.data(), tbv.size());
-}
-
-void AndroidBugreportParser::NotifyEndOfFile() {
+util::Status AndroidBugreportParser::ParseImpl() {
+ // All logs in Android bugreports use wall time (which creates problems
+ // in case of early boot events before NTP kicks in, which get emitted as
+ // 1970), but that is the state of affairs.
+ context_->clock_tracker->SetTraceTimeClock(
+ protos::pbzero::BUILTIN_CLOCK_REALTIME);
if (!DetectYearAndBrFilename()) {
context_->storage->IncrementStats(stats::android_br_parse_errors);
- return;
+ return base::ErrStatus("Zip file does not contain bugreport file.");
}
ParsePersistentLogcat();
ParseDumpstateTxt();
SortAndStoreLogcat();
+ return base::OkStatus();
}
void AndroidBugreportParser::ParseDumpstateTxt() {
+ PERFETTO_CHECK(dumpstate_file_);
// Dumpstate is organized in a two level hierarchy, beautifully flattened into
// one text file with load bearing ----- markers:
// 1. Various dumpstate sections, examples:
@@ -95,80 +131,81 @@
// Here we put each line in a dedicated table, android_dumpstate, keeping
// track of the dumpstate `section` and dumpsys `service`.
AndroidLogParser log_parser(br_year_, context_->storage.get());
- util::ZipFile* zf = zip_reader_->Find(dumpstate_fname_);
StringId section_id = StringId::Null(); // The current dumpstate section.
StringId service_id = StringId::Null(); // The current dumpsys service.
static constexpr size_t npos = base::StringView::npos;
enum { OTHER = 0, DUMPSYS, LOG } cur_sect = OTHER;
- zf->DecompressLines([&](const std::vector<base::StringView>& lines) {
- // Optimization for ParseLogLines() below. Avoids ctor/dtor-ing a new vector
- // on every line.
- std::vector<base::StringView> log_line(1);
- for (const base::StringView& line : lines) {
- if (line.StartsWith("------ ") && line.EndsWith(" ------")) {
- // These lines mark the beginning and end of dumpstate sections:
- // ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
- // ------ 0.356s was the duration of 'DUMPSYS CRITICAL' ------
- base::StringView section = line.substr(7);
- section = section.substr(0, section.size() - 7);
- bool end_marker = section.find("was the duration of") != npos;
- service_id = StringId::Null();
- if (end_marker) {
- section_id = StringId::Null();
- } else {
- section_id = context_->storage->InternString(section);
- cur_sect = OTHER;
- if (section.StartsWith("DUMPSYS")) {
- cur_sect = DUMPSYS;
- } else if (section.StartsWith("SYSTEM LOG") ||
- section.StartsWith("EVENT LOG") ||
- section.StartsWith("RADIO LOG")) {
- // KERNEL LOG is deliberately omitted because SYSTEM LOG is a
- // superset. KERNEL LOG contains all dupes.
- cur_sect = LOG;
- } else if (section.StartsWith("BLOCK STAT")) {
- // Coalesce all the block stats into one section. Otherwise they
- // pollute the table with one section per block device.
- section_id = context_->storage->InternString("BLOCK STAT");
+ dumpstate_file_->DecompressLines(
+ [&](const std::vector<base::StringView>& lines) {
+ // Optimization for ParseLogLines() below. Avoids ctor/dtor-ing a new
+ // vector on every line.
+ std::vector<base::StringView> log_line(1);
+ for (const base::StringView& line : lines) {
+ if (line.StartsWith("------ ") && line.EndsWith(" ------")) {
+ // These lines mark the beginning and end of dumpstate sections:
+ // ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
+ // ------ 0.356s was the duration of 'DUMPSYS CRITICAL' ------
+ base::StringView section = line.substr(7);
+ section = section.substr(0, section.size() - 7);
+ bool end_marker = section.find("was the duration of") != npos;
+ service_id = StringId::Null();
+ if (end_marker) {
+ section_id = StringId::Null();
+ } else {
+ section_id = context_->storage->InternString(section);
+ cur_sect = OTHER;
+ if (section.StartsWith("DUMPSYS")) {
+ cur_sect = DUMPSYS;
+ } else if (section.StartsWith("SYSTEM LOG") ||
+ section.StartsWith("EVENT LOG") ||
+ section.StartsWith("RADIO LOG")) {
+ // KERNEL LOG is deliberately omitted because SYSTEM LOG is a
+ // superset. KERNEL LOG contains all dupes.
+ cur_sect = LOG;
+ } else if (section.StartsWith("BLOCK STAT")) {
+ // Coalesce all the block stats into one section. Otherwise they
+ // pollute the table with one section per block device.
+ section_id = context_->storage->InternString("BLOCK STAT");
+ }
+ }
+ continue;
}
+ // Skip end marker lines for dumpsys sections.
+ if (cur_sect == DUMPSYS && line.StartsWith("--------- ") &&
+ line.find("was the duration of dumpsys") != npos) {
+ service_id = StringId::Null();
+ continue;
+ }
+ if (cur_sect == DUMPSYS && service_id.is_null() &&
+ line.StartsWith(
+ "----------------------------------------------")) {
+ continue;
+ }
+ if (cur_sect == DUMPSYS && line.StartsWith("DUMP OF SERVICE")) {
+ // DUMP OF SERVICE [CRITICAL|HIGH] ServiceName:
+ base::StringView svc = line.substr(line.rfind(' ') + 1);
+ svc = svc.substr(0, svc.size() - 1);
+ service_id = context_->storage->InternString(svc);
+ } else if (cur_sect == LOG) {
+ // Parse the non-persistent logcat and append to `log_events_`,
+ // together with the persistent one previously parsed by
+ // ParsePersistentLogcat(). Skips entries that are already seen in
+ // the persistent logcat, handling us vs ms truncation.
+ PERFETTO_DCHECK(log_line.size() == 1);
+ log_line[0] = line;
+ log_parser.ParseLogLines(log_line, &log_events_,
+ log_events_last_sorted_idx_);
+ }
+
+ if (build_fpr_.empty() && line.StartsWith("Build fingerprint:")) {
+ build_fpr_ = line.substr(20, line.size() - 20).ToStdString();
+ }
+
+ // Append the line to the android_dumpstate table.
+ context_->storage->mutable_android_dumpstate_table()->Insert(
+ {section_id, service_id, context_->storage->InternString(line)});
}
- continue;
- }
- // Skip end marker lines for dumpsys sections.
- if (cur_sect == DUMPSYS && line.StartsWith("--------- ") &&
- line.find("was the duration of dumpsys") != npos) {
- service_id = StringId::Null();
- continue;
- }
- if (cur_sect == DUMPSYS && service_id.is_null() &&
- line.StartsWith("----------------------------------------------")) {
- continue;
- }
- if (cur_sect == DUMPSYS && line.StartsWith("DUMP OF SERVICE")) {
- // DUMP OF SERVICE [CRITICAL|HIGH] ServiceName:
- base::StringView svc = line.substr(line.rfind(' ') + 1);
- svc = svc.substr(0, svc.size() - 1);
- service_id = context_->storage->InternString(svc);
- } else if (cur_sect == LOG) {
- // Parse the non-persistent logcat and append to `log_events_`, together
- // with the persistent one previously parsed by ParsePersistentLogcat().
- // Skips entries that are already seen in the persistent logcat,
- // handling us vs ms truncation.
- PERFETTO_DCHECK(log_line.size() == 1);
- log_line[0] = line;
- log_parser.ParseLogLines(log_line, &log_events_,
- log_events_last_sorted_idx_);
- }
-
- if (build_fpr_.empty() && line.StartsWith("Build fingerprint:")) {
- build_fpr_ = line.substr(20, line.size() - 20).ToStdString();
- }
-
- // Append the line to the android_dumpstate table.
- context_->storage->mutable_android_dumpstate_table()->Insert(
- {section_id, service_id, context_->storage->InternString(line)});
- }
- });
+ });
}
void AndroidBugreportParser::ParsePersistentLogcat() {
@@ -182,22 +219,23 @@
// Sort files to ease the job of the subsequent line-based sort. Unfortunately
// lines within each file are not 100% timestamp-ordered, due to things like
// kernel messages where log time != event time.
- std::vector<std::pair<uint64_t, std::string>> log_paths;
- for (const util::ZipFile& zf : zip_reader_->files()) {
+ std::vector<std::pair<uint64_t, const util::ZipFile*>> log_files;
+ for (const util::ZipFile& zf : zip_file_entries_) {
if (base::StartsWith(zf.name(), "FS/data/misc/logd/logcat") &&
!base::EndsWith(zf.name(), "logcat.id")) {
- log_paths.emplace_back(std::make_pair(zf.GetDatetime(), zf.name()));
+ log_files.push_back(std::make_pair(zf.GetDatetime(), &zf));
}
}
- std::sort(log_paths.begin(), log_paths.end());
+
+ std::sort(log_files.begin(), log_files.end());
// Push all events into the AndroidLogParser. It will take care of string
// interning into the pool. Appends entries into `log_events`.
- for (const auto& kv : log_paths) {
- util::ZipFile* zf = zip_reader_->Find(kv.second);
- zf->DecompressLines([&](const std::vector<base::StringView>& lines) {
- log_parser.ParseLogLines(lines, &log_events_);
- });
+ for (const auto& log_file : log_files) {
+ log_file.second->DecompressLines(
+ [&](const std::vector<base::StringView>& lines) {
+ log_parser.ParseLogLines(lines, &log_events_);
+ });
}
// Do an initial sorting pass. This is not the final sorting because we
@@ -230,30 +268,20 @@
// This is obviously bugged for cases of bugreports collected across new year
// but we'll live with that.
bool AndroidBugreportParser::DetectYearAndBrFilename() {
- const util::ZipFile* br_file = nullptr;
- for (const auto& zf : zip_reader_->files()) {
- if (base::StartsWith(zf.name(), "bugreport-") &&
- base::EndsWith(zf.name(), ".txt")) {
- br_file = &zf;
- break;
- }
- }
-
+ const util::ZipFile* br_file = FindBugReportFile(zip_file_entries_);
if (!br_file) {
PERFETTO_ELOG("Could not find bugreport-*.txt in the zip file");
return false;
}
- // Typical name: "bugreport-product-TP1A.220623.001-2022-06-24-16-24-37.txt".
- auto year_str = br_file->name().substr(
- br_file->name().size() - strlen("2022-12-31-23-59-00.txt"), 4);
- std::optional<int32_t> year = base::StringToInt32(year_str);
+ std::optional<int32_t> year =
+ ExtractYearFromBugReportFilename(br_file->name());
if (!year.has_value()) {
PERFETTO_ELOG("Could not parse the year from %s", br_file->name().c_str());
return false;
}
br_year_ = *year;
- dumpstate_fname_ = br_file->name();
+ dumpstate_file_ = br_file;
return true;
}
diff --git a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
index 9969159..a9ca322 100644
--- a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
+++ b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
@@ -17,8 +17,11 @@
#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_ANDROID_BUGREPORT_ANDROID_BUGREPORT_PARSER_H_
#define SRC_TRACE_PROCESSOR_IMPORTERS_ANDROID_BUGREPORT_ANDROID_BUGREPORT_PARSER_H_
-#include "src/trace_processor/importers/common/chunked_trace_reader.h"
-#include "src/trace_processor/storage/trace_storage.h"
+#include <cstddef>
+#include <vector>
+
+#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/util/zip_reader.h"
namespace perfetto {
namespace trace_processor {
@@ -31,16 +34,19 @@
class TraceProcessorContext;
// Trace importer for Android bugreport.zip archives.
-class AndroidBugreportParser : public ChunkedTraceReader {
+class AndroidBugreportParser {
public:
- explicit AndroidBugreportParser(TraceProcessorContext*);
- ~AndroidBugreportParser() override;
-
- // ChunkedTraceReader implementation.
- util::Status Parse(TraceBlobView) override;
- void NotifyEndOfFile() override;
+ static bool IsAndroidBugReport(
+ const std::vector<util::ZipFile>& zip_file_entries);
+ static util::Status Parse(TraceProcessorContext* context,
+ std::vector<util::ZipFile> zip_file_entries);
private:
+ AndroidBugreportParser(TraceProcessorContext* context,
+ std::vector<util::ZipFile> zip_file_entries);
+ ~AndroidBugreportParser();
+ util::Status ParseImpl();
+
bool DetectYearAndBrFilename();
void ParsePersistentLogcat();
void ParseDumpstateTxt();
@@ -48,11 +54,11 @@
void SortLogEvents();
TraceProcessorContext* const context_;
+ std::vector<util::ZipFile> zip_file_entries_;
int br_year_ = 0; // The year when the bugreport has been taken.
- std::string dumpstate_fname_; // The name of bugreport-xxx-2022-08-04....txt
+ const util::ZipFile* dumpstate_file_ =
+ nullptr; // The bugreport-xxx-2022-08-04....txt file
std::string build_fpr_;
- bool first_chunk_seen_ = false;
- std::unique_ptr<util::ZipReader> zip_reader_;
std::vector<AndroidLogEvent> log_events_;
size_t log_events_last_sorted_idx_ = 0;
};
diff --git a/src/trace_processor/importers/proto/gpu_event_parser.cc b/src/trace_processor/importers/proto/gpu_event_parser.cc
index d08d946..5664fba 100644
--- a/src/trace_processor/importers/proto/gpu_event_parser.cc
+++ b/src/trace_processor/importers/proto/gpu_event_parser.cc
@@ -359,6 +359,16 @@
}
}
+ if (event.has_context()) {
+ uint64_t context_id = event.context();
+ auto* decoder = sequence_state->LookupInternedMessage<
+ protos::pbzero::InternedData::kGraphicsContextsFieldNumber,
+ protos::pbzero::InternedGraphicsContext>(context_id);
+ if (decoder) {
+ pid = decoder->pid();
+ }
+ }
+
auto args_callback = [this, &event,
sequence_state](ArgsTracker::BoundInserter* inserter) {
if (event.has_stage_iid()) {
diff --git a/src/trace_processor/importers/zip/BUILD.gn b/src/trace_processor/importers/zip/BUILD.gn
new file mode 100644
index 0000000..a938fdd
--- /dev/null
+++ b/src/trace_processor/importers/zip/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 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.
+
+source_set("full") {
+ sources = [
+ "zip_trace_reader.cc",
+ "zip_trace_reader.h",
+ ]
+ deps = [
+ "../../../../gn:default_deps",
+ "../../../../gn:zlib",
+ "../../../../include/perfetto/ext/base:base",
+ "../../../trace_processor:storage_minimal",
+ "../../types",
+ "../../util:trace_type",
+ "../../util:util",
+ "../../util:zip_reader",
+ "../android_bugreport",
+ "../common",
+ "../proto:minimal",
+ ]
+}
diff --git a/src/trace_processor/importers/zip/zip_trace_reader.cc b/src/trace_processor/importers/zip/zip_trace_reader.cc
new file mode 100644
index 0000000..20f019f
--- /dev/null
+++ b/src/trace_processor/importers/zip/zip_trace_reader.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 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 "src/trace_processor/importers/zip/zip_trace_reader.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/forwarding_trace_parser.h"
+#include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h"
+#include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/trace_type.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+// Proto traces should always parsed first as they might contains clock sync
+// data needed to correctly parse other traces.
+// The rest of the types are sorted by position in the enum but this is not
+// something users should rely on.
+// TODO(carlscab): Proto traces with just ModuleSymbols packets should be an
+// exception. We actually need those are the very end (once whe have all the
+// Frames). Alternatively we could build a map address -> symbol during
+// tokenization and use this during parsing to resolve symbols.
+bool CompareTraceType(TraceType lhs, TraceType rhs) {
+ if (rhs == TraceType::kProtoTraceType) {
+ return false;
+ }
+ if (lhs == TraceType::kProtoTraceType) {
+ return true;
+ }
+ return lhs < rhs;
+}
+
+bool HasSymbols(const TraceBlobView& blob) {
+ bool has_symbols = false;
+ ProtoTraceTokenizer().Tokenize(blob.copy(), [&](TraceBlobView raw) {
+ protos::pbzero::TracePacket::Decoder packet(raw.data(), raw.size());
+ has_symbols = packet.has_module_symbols();
+ return base::ErrStatus("break");
+ });
+ return has_symbols;
+}
+
+} // namespace
+
+ZipTraceReader::ZipTraceReader(TraceProcessorContext* context)
+ : context_(context) {}
+ZipTraceReader::~ZipTraceReader() = default;
+
+bool ZipTraceReader::Entry::operator<(const Entry& rhs) const {
+ // Traces with symbols should be the last ones to be read.
+ if (has_symbols) {
+ return false;
+ }
+ if (rhs.has_symbols) {
+ return true;
+ }
+ if (CompareTraceType(trace_type, rhs.trace_type)) {
+ return true;
+ }
+ if (CompareTraceType(rhs.trace_type, trace_type)) {
+ return false;
+ }
+ return std::tie(name, index) < std::tie(rhs.name, rhs.index);
+}
+
+util::Status ZipTraceReader::Parse(TraceBlobView blob) {
+ zip_reader_.Parse(blob.data(), blob.size());
+ return base::OkStatus();
+}
+
+void ZipTraceReader::NotifyEndOfFile() {
+ base::Status status = NotifyEndOfFileImpl();
+ if (!status.ok()) {
+ PERFETTO_ELOG("ZipTraceReader failed: %s", status.c_message());
+ }
+}
+
+base::Status ZipTraceReader::NotifyEndOfFileImpl() {
+ std::vector<util::ZipFile> files = zip_reader_.TakeFiles();
+
+ // Android bug reports are ZIP files and its files do not get handled
+ // separately.
+ if (AndroidBugreportParser::IsAndroidBugReport(files)) {
+ return AndroidBugreportParser::Parse(context_, std::move(files));
+ }
+
+ base::StatusOr<std::vector<Entry>> entries = ExtractEntries(std::move(files));
+ if (!entries.ok()) {
+ return entries.status();
+ }
+ std::sort(entries->begin(), entries->end());
+
+ for (Entry& e : *entries) {
+ parsers_.push_back(std::make_unique<ForwardingTraceParser>(context_));
+ auto& parser = *parsers_.back();
+ RETURN_IF_ERROR(parser.Parse(std::move(e.uncompressed_data)));
+ parser.NotifyEndOfFile();
+ }
+ return base::OkStatus();
+}
+
+base::StatusOr<std::vector<ZipTraceReader::Entry>>
+ZipTraceReader::ExtractEntries(std::vector<util::ZipFile> files) const {
+ // TODO(carlsacab): There is a lot of unnecessary copying going on here.
+ // ZipTraceReader can directly parse the ZIP file and given that we know the
+ // decompressed size we could directly decompress into TraceBlob chunks and
+ // send them to the tokenizer.
+ std::vector<Entry> entries;
+ std::vector<uint8_t> buffer;
+ for (size_t i = 0; i < files.size(); ++i) {
+ const util::ZipFile& zip_file = files[i];
+ Entry entry;
+ entry.name = zip_file.name();
+ entry.index = i;
+ RETURN_IF_ERROR(files[i].Decompress(&buffer));
+ entry.uncompressed_data =
+ TraceBlobView(TraceBlob::CopyFrom(buffer.data(), buffer.size()));
+ entry.trace_type = GuessTraceType(entry.uncompressed_data.data(),
+ entry.uncompressed_data.size());
+ entry.has_symbols = entry.trace_type == TraceType::kProtoTraceType &&
+ HasSymbols(entry.uncompressed_data);
+ entries.push_back(std::move(entry));
+ }
+ return std::move(entries);
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/zip/zip_trace_reader.h b/src/trace_processor/importers/zip/zip_trace_reader.h
new file mode 100644
index 0000000..d4f3d88
--- /dev/null
+++ b/src/trace_processor/importers/zip/zip_trace_reader.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_ZIP_ZIP_TRACE_READER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_ZIP_ZIP_TRACE_READER_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/chunked_trace_reader.h"
+#include "src/trace_processor/util/trace_type.h"
+#include "src/trace_processor/util/zip_reader.h"
+
+namespace perfetto::trace_processor {
+
+class ForwardingTraceParser;
+class TraceProcessorContext;
+
+// Forwards files contained in a ZIP to the appropiate ChunkedTraceReader. It is
+// guaranteed that proto traces will be parsed first.
+class ZipTraceReader : public ChunkedTraceReader {
+ public:
+ explicit ZipTraceReader(TraceProcessorContext* context);
+ ~ZipTraceReader() override;
+
+ // ChunkedTraceReader implementation
+ util::Status Parse(TraceBlobView) override;
+ void NotifyEndOfFile() override;
+
+ private:
+ // Represents a file in the ZIP file. Used to sort them before sending the
+ // files one by one to a `ForwardingTraceParser` instance.
+ struct Entry {
+ // File name. Used to break ties.
+ std::string name;
+ // Position in the zip file. Used to break ties.
+ size_t index;
+ // Trace type. This is the main attribute traces are ordered by. Proto
+ // traces are always parsed first as they might contains clock sync
+ // data needed to correctly parse other traces.
+ TraceType trace_type;
+ TraceBlobView uncompressed_data;
+ // True for proto trace_types whose fist message is a ModuleSymbols packet
+ bool has_symbols = false;
+ // Comparator used to determine the order in which files in the ZIP will be
+ // read.
+ bool operator<(const Entry& rhs) const;
+ };
+
+ base::Status NotifyEndOfFileImpl();
+ base::StatusOr<std::vector<Entry>> ExtractEntries(
+ std::vector<util::ZipFile> files) const;
+ base::Status ParseEntry(Entry entry);
+
+ TraceProcessorContext* const context_;
+ util::ZipReader zip_reader_;
+ // For every file in the ZIP we will create a `ForwardingTraceParser`instance
+ // and send that file to it for tokenization. The instances are kept around
+ // here as some tokenizers might keep state that is later needed after
+ // sorting.
+ std::vector<std::unique_ptr<ForwardingTraceParser>> parsers_;
+};
+
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_ZIP_ZIP_TRACE_READER_H_
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index 95d050b..2e2e48e 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -514,7 +514,7 @@
)
),
- 'slow_start_reason_detailed', get_slow_start_reason_detailed(launches.startup_id)
+ 'slow_start_reason_with_details', get_slow_start_reason_with_details(launches.startup_id)
) AS startup
FROM android_startups launches;
diff --git a/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql b/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql
index 2a673c0..8108fa6 100644
--- a/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql
+++ b/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql
@@ -201,110 +201,130 @@
ORDER BY slice_dur DESC
LIMIT 1;
-
-CREATE OR REPLACE PERFETTO FUNCTION get_slow_start_reason_detailed(startup_id LONG)
+CREATE OR REPLACE PERFETTO FUNCTION get_slow_start_reason_with_details(startup_id LONG)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_SlowStartReasonDetailed(
+ SELECT RepeatedField(AndroidStartupMetric_SlowStartReason(
+ 'reason_id', reason_id,
'reason', slow_cause,
- 'details', details))
+ 'launch_dur', launch_dur))
FROM (
SELECT 'No baseline or cloud profiles' AS slow_cause,
- get_missing_baseline_profile_for_launch(launch.startup_id, launch.package) as details
+ launch.dur as launch_dur,
+ 'NO_BASELINE_OR_CLOUD_PROFILES' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND missing_baseline_profile_for_launch(launch.startup_id, launch.package)
UNION ALL
- SELECT 'Optimized artifacts missing, run from apk' as slow_cause, NULL as details
+ SELECT 'Optimized artifacts missing, run from apk' as slow_cause,
+ launch.dur as launch_dur,
+ 'RUN_FROM_APK' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
- AND run_from_apk_for_launch(launch.startup_id)
+ AND run_from_apk_for_launch(launch.startup_id)
UNION ALL
- SELECT 'Unlock running during launch' as slow_cause, NULL as details
+ SELECT 'Unlock running during launch' as slow_cause,
+ launch.dur as launch_dur,
+ 'UNLOCK_RUNNING' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND is_unlock_running_during_launch(launch.startup_id)
UNION ALL
- SELECT 'App in debuggable mode' as slow_cause, NULL as details
- FROM android_startups launch
+ SELECT 'App in debuggable mode' as slow_cause,
+ launch.dur as launch_dur,
+ 'APP_IN_DEBUGGABLE_MODE' as reason_id
+ FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND is_process_debuggable(launch.package)
UNION ALL
- SELECT 'GC Activity' as slow_cause, NULL as details
+ SELECT 'GC Activity' as slow_cause,
+ launch.dur as launch_dur,
+ 'GC_ACTIVITY' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND total_gc_time_by_launch(launch.startup_id) > 0
UNION ALL
- SELECT 'dex2oat running during launch' AS slow_cause, NULL as details
+ SELECT 'dex2oat running during launch' AS slow_cause,
+ launch.dur as launch_dur,
+ 'DEX2OAT_RUNNING' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id AND
dur_of_process_running_concurrent_to_launch(launch.startup_id, '*dex2oat64') > 0
UNION ALL
- SELECT 'installd running during launch' AS slow_cause, NULL as details
+ SELECT 'installd running during launch' AS slow_cause,
+ launch.dur as launch_dur,
+ 'INSTALLD_RUNNING' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id AND
dur_of_process_running_concurrent_to_launch(launch.startup_id, '*installd') > 0
UNION ALL
SELECT 'Main Thread - Time spent in Runnable state' as slow_cause,
- get_main_thread_time_for_launch_in_runnable_state(
- launch.startup_id, launch.dur) as details
+ launch.dur as launch_dur,
+ 'MAIN_THREAD_TIME_SPENT_IN_RUNNABLE' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND main_thread_time_for_launch_in_runnable_state(launch.startup_id) > launch.dur * 0.15
UNION ALL
- SELECT 'Main Thread - Time spent in interruptible sleep state'
- AS slow_cause, NULL as details
+ SELECT 'Main Thread - Time spent in interruptible sleep state' as slow_cause,
+ launch.dur as launch_dur,
+ 'MAIN_THREAD_TIME_SPENT_IN_INTERRUPTIBLE_SLEEP' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND main_thread_time_for_launch_and_state(launch.startup_id, 'S') > 2900e6
UNION ALL
- SELECT 'Main Thread - Time spent in Blocking I/O' as slow_cause, NULL as details
+ SELECT 'Main Thread - Time spent in Blocking I/O' as slow_cause,
+ launch.dur as launch_dur,
+ 'MAIN_THREAD_TIME_SPENT_IN_BLOCKING_IO' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND main_thread_time_for_launch_state_and_io_wait(launch.startup_id, 'D*', TRUE) > 450e6
UNION ALL
SELECT 'Main Thread - Time spent in OpenDexFilesFromOat*' as slow_cause,
- get_android_sum_dur_on_main_thread_for_startup_and_slice(
- launch.startup_id, 'OpenDexFilesFromOat*', launch.dur) as details
+ launch.dur as launch_dur,
+ 'MAIN_THREAD_TIME_SPENT_IN_OPEN_DEX_FILES_FROM_OAT' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id AND
android_sum_dur_on_main_thread_for_startup_and_slice(
launch.startup_id, 'OpenDexFilesFromOat*') > launch.dur * 0.2
UNION ALL
- SELECT 'Time spent in bindApplication'
- AS slow_cause, NULL as details
+ SELECT 'Time spent in bindApplication' as slow_cause,
+ launch.dur as launch_dur,
+ 'TIME_SPENT_IN_BIND_APPLICATION' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND android_sum_dur_for_startup_and_slice(launch.startup_id, 'bindApplication') > 1250e6
UNION ALL
- SELECT 'Time spent in view inflation' as slow_cause, NULL as details
+ SELECT 'Time spent in view inflation' as slow_cause,
+ launch.dur as launch_dur,
+ 'TIME_SPENT_IN_VIEW_INFLATION' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND android_sum_dur_for_startup_and_slice(launch.startup_id, 'inflate') > 450e6
UNION ALL
SELECT 'Time spent in ResourcesManager#getResources' as slow_cause,
- get_android_sum_dur_for_startup_and_slice(
- launch.startup_id, 'ResourcesManager#getResources', 130) as details
+ launch.dur as launch_dur,
+ 'TIME_SPENT_IN_RESOURCES_MANAGER_GET_RESOURCES' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND android_sum_dur_for_startup_and_slice(
launch.startup_id, 'ResourcesManager#getResources') > 130e6
UNION ALL
- SELECT 'Time spent verifying classes'
- AS slow_cause, NULL as details
+ SELECT 'Time spent verifying classes' as slow_cause,
+ launch.dur as launch_dur,
+ 'TIME_SPENT_VERIFYING_CLASSES' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id AND
android_sum_dur_for_startup_and_slice(launch.startup_id, 'VerifyClass*')
@@ -312,7 +332,8 @@
UNION ALL
SELECT 'Potential CPU contention with another process' AS slow_cause,
- get_potential_cpu_contention_with_another_process(launch.startup_id) as details
+ launch.dur as launch_dur,
+ 'POTENTIAL_CPU_CONTENTION_WITH_ANOTHER_PROCESS' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id AND
main_thread_time_for_launch_in_runnable_state(launch.startup_id) > 100e6 AND
@@ -320,7 +341,8 @@
UNION ALL
SELECT 'JIT Activity' as slow_cause,
- get_jit_activity(launch.startup_id) as details
+ launch.dur as launch_dur,
+ 'JIT_ACTIVITY' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND thread_time_for_launch_state_and_thread(
@@ -330,8 +352,9 @@
) > 100e6
UNION ALL
- SELECT 'Main Thread - Lock contention'
- AS slow_cause, NULL as details
+ SELECT 'Main Thread - Lock contention' as slow_cause,
+ launch.dur as launch_dur,
+ 'MAIN_THREAD_LOCK_CONTENTION' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND android_sum_dur_on_main_thread_for_startup_and_slice(
@@ -340,8 +363,9 @@
) > launch.dur * 0.2
UNION ALL
- SELECT 'Main Thread - Monitor contention'
- AS slow_cause, NULL as details
+ SELECT 'Main Thread - Monitor contention' as slow_cause,
+ launch.dur as launch_dur,
+ 'MAIN_THREAD_MONITOR_CONTENTION' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND android_sum_dur_on_main_thread_for_startup_and_slice(
@@ -350,51 +374,55 @@
) > launch.dur * 0.15
UNION ALL
- SELECT 'JIT compiled methods' as slow_cause, NULL as details
+ SELECT 'JIT compiled methods' as slow_cause,
+ launch.dur as launch_dur,
+ 'JIT_COMPILED_METHODS' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND (
SELECT COUNT(1)
FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(launch.startup_id, 'JIT compiling*')
- WHERE thread_name = 'Jit thread pool'
- ) > 65
+ WHERE thread_name = 'Jit thread pool') > 65
UNION ALL
- SELECT 'Broadcast dispatched count' as slow_cause, NULL as details
+ SELECT 'Broadcast dispatched count' as slow_cause,
+ launch.dur as launch_dur,
+ 'BROADCAST_DISPATCHED_COUNT' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND count_slices_concurrent_to_launch(
launch.startup_id,
- 'Broadcast dispatched*'
- ) > 15
+ 'Broadcast dispatched*') > 15
UNION ALL
- SELECT 'Broadcast received count' as slow_cause, NULL as details
+ SELECT 'Broadcast received count' as slow_cause,
+ launch.dur as launch_dur,
+ 'BROADCAST_RECEIVED_COUNT' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND count_slices_concurrent_to_launch(
launch.startup_id,
- 'broadcastReceiveReg*'
- ) > 50
+ 'broadcastReceiveReg*') > 50
UNION ALL
- SELECT 'Startup running concurrent to launch' as slow_cause, NULL as details
+ SELECT 'Startup running concurrent to launch' as slow_cause,
+ launch.dur as launch_dur,
+ 'STARTUP_RUNNING_CONCURRENT' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND EXISTS(
SELECT package
FROM android_startups l
WHERE l.startup_id != launch.startup_id
- AND _is_spans_overlapping(l.ts, l.ts_end, launch.ts, launch.ts_end)
- )
+ AND _is_spans_overlapping(l.ts, l.ts_end, launch.ts, launch.ts_end))
UNION ALL
SELECT 'Main Thread - Binder transactions blocked' as slow_cause,
- get_main_thread_binder_transactions_blocked(launch.startup_id, 2e7) as details
+ launch.dur as launch_dur,
+ 'MAIN_THREAD_BINDER_TRANSCATIONS_BLOCKED' as reason_id
FROM android_startups launch
WHERE launch.startup_id = $startup_id
AND (
SELECT COUNT(1)
- FROM BINDER_TRANSACTION_REPLY_SLICES_FOR_LAUNCH(launch.startup_id, 2e7)
- ) > 0
- );
+ FROM BINDER_TRANSACTION_REPLY_SLICES_FOR_LAUNCH(launch.startup_id, 2e7)) > 0
+ );
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
index 8f163a0..3048e68 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
@@ -32,6 +32,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
+#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
@@ -848,7 +849,18 @@
const std::vector<std::string>& column_names,
const std::vector<sql_argument::ArgumentDefinition>& schema,
const char* tag) {
- // If the user has not provided a schema, we have nothing to validate.
+ std::vector<std::string> duplicate_columns;
+ for (auto it = column_names.begin(); it != column_names.end(); ++it) {
+ if (std::count(it + 1, column_names.end(), *it) > 0) {
+ duplicate_columns.push_back(*it);
+ }
+ }
+ if (!duplicate_columns.empty()) {
+ return base::ErrStatus("%s: multiple columns are named: %s", tag,
+ base::Join(duplicate_columns, ", ").c_str());
+ }
+
+ // If the user has not provided a schema, we have nothing further to validate.
if (schema.empty()) {
return base::OkStatus();
}
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index d09c8a7..731f4e3 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -14,17 +14,23 @@
* limitations under the License.
*/
-#include <algorithm>
+#include <cstdint>
#include <cstdio>
-#include <map>
-#include <optional>
+#include <cstring>
+#include <memory>
#include <random>
#include <string>
+#include <utility>
+#include <vector>
+#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/iterator.h"
+#include "perfetto/trace_processor/status.h"
#include "perfetto/trace_processor/trace_processor.h"
#include "protos/perfetto/common/descriptor.pbzero.h"
#include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
@@ -32,11 +38,12 @@
#include "src/base/test/utils.h"
#include "test/gtest_and_gmock.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
namespace {
-constexpr size_t kMaxChunkSize = 4 * 1024 * 1024;
+using testing::HasSubstr;
+
+constexpr size_t kMaxChunkSize = 4ul * 1024 * 1024;
TEST(TraceProcessorCustomConfigTest, SkipInternalMetricsMatchingMountPath) {
auto config = Config();
@@ -97,12 +104,13 @@
: processor_(TraceProcessor::CreateInstance(Config())) {}
protected:
- util::Status LoadTrace(const char* name,
+ base::Status LoadTrace(const char* name,
size_t min_chunk_size = 512,
size_t max_chunk_size = kMaxChunkSize) {
EXPECT_LE(min_chunk_size, max_chunk_size);
- base::ScopedFstream f(fopen(
- base::GetTestDataPath(std::string("test/data/") + name).c_str(), "rb"));
+ base::ScopedFstream f(
+ fopen(base::GetTestDataPath(std::string("test/data/") + name).c_str(),
+ "rbe"));
std::minstd_rand0 rnd_engine(0);
std::uniform_int_distribution<size_t> dist(min_chunk_size, max_chunk_size);
while (!feof(*f)) {
@@ -118,7 +126,7 @@
}
Iterator Query(const std::string& query) {
- return processor_->ExecuteQuery(query.c_str());
+ return processor_->ExecuteQuery(query);
}
TraceProcessor* Processor() { return processor_.get(); }
@@ -280,7 +288,7 @@
TEST_F(TraceProcessorIntegrationTest, ComputeMetricsFormattedExtension) {
std::string metric_output;
- util::Status status = Processor()->ComputeMetricText(
+ base::Status status = Processor()->ComputeMetricText(
std::vector<std::string>{"test_chrome_metric"},
TraceProcessor::MetricResultFormat::kProtoText, &metric_output);
ASSERT_TRUE(status.ok());
@@ -293,7 +301,7 @@
TEST_F(TraceProcessorIntegrationTest, ComputeMetricsFormattedNoExtension) {
std::string metric_output;
- util::Status status = Processor()->ComputeMetricText(
+ base::Status status = Processor()->ComputeMetricText(
std::vector<std::string>{"trace_metadata"},
TraceProcessor::MetricResultFormat::kProtoText, &metric_output);
ASSERT_TRUE(status.ok());
@@ -321,21 +329,21 @@
}
TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14762) {
- ASSERT_TRUE(LoadTrace("clusterfuzz_14762", 4096 * 1024).ok());
+ ASSERT_TRUE(LoadTrace("clusterfuzz_14762", 4096ul * 1024).ok());
auto it = Query("select sum(value) from stats where severity = 'error';");
ASSERT_TRUE(it.Next());
ASSERT_GT(it.Get(0).long_value, 0);
}
TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14767) {
- ASSERT_TRUE(LoadTrace("clusterfuzz_14767", 4096 * 1024).ok());
+ ASSERT_TRUE(LoadTrace("clusterfuzz_14767", 4096ul * 1024).ok());
auto it = Query("select sum(value) from stats where severity = 'error';");
ASSERT_TRUE(it.Next());
ASSERT_GT(it.Get(0).long_value, 0);
}
TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14799) {
- ASSERT_TRUE(LoadTrace("clusterfuzz_14799", 4096 * 1024).ok());
+ ASSERT_TRUE(LoadTrace("clusterfuzz_14799", 4096ul * 1024).ok());
auto it = Query("select sum(value) from stats where severity = 'error';");
ASSERT_TRUE(it.Next());
ASSERT_GT(it.Get(0).long_value, 0);
@@ -793,6 +801,15 @@
ASSERT_TRUE(it.Status().ok());
}
+TEST_F(TraceProcessorIntegrationTest, CreateTableDuplicateNames) {
+ auto it = Query(
+ "create perfetto table foo select 1 as duplicate_a, 2 as duplicate_a, 3 "
+ "as duplicate_b, 4 as duplicate_b");
+ ASSERT_FALSE(it.Next());
+ ASSERT_FALSE(it.Status().ok());
+ ASSERT_THAT(it.Status().message(), HasSubstr("duplicate_a"));
+ ASSERT_THAT(it.Status().message(), HasSubstr("duplicate_b"));
+}
+
} // namespace
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index fb9b294..117a737 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -59,6 +59,7 @@
#include "src/trace_processor/importers/proto/additional_modules.h"
#include "src/trace_processor/importers/proto/content_analyzer.h"
#include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
+#include "src/trace_processor/importers/zip/zip_trace_reader.h"
#include "src/trace_processor/iterator_impl.h"
#include "src/trace_processor/metrics/all_chrome_metrics.descriptor.h"
#include "src/trace_processor/metrics/all_webview_metrics.descriptor.h"
@@ -121,6 +122,7 @@
#include "src/trace_processor/util/regex.h"
#include "src/trace_processor/util/sql_modules.h"
#include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/trace_type.h"
#include "protos/perfetto/common/builtin_clock.pbzero.h"
#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
@@ -308,8 +310,8 @@
return "ctrace";
case kNinjaLogTraceType:
return "ninja_log";
- case kAndroidBugreportTraceType:
- return "android_bugreport";
+ case kZipFile:
+ return "zip";
case kPerfDataTraceType:
return "perf_data";
}
@@ -362,8 +364,7 @@
kGzipTraceType);
context_.reader_registry->RegisterTraceReader<GzipTraceParser>(
kCtraceTraceType);
- context_.reader_registry->RegisterTraceReader<AndroidBugreportParser>(
- kAndroidBugreportTraceType);
+ context_.reader_registry->RegisterTraceReader<ZipTraceReader>(kZipFile);
}
if (json::IsJsonSupported()) {
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 32f2452..09d5386 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -107,6 +107,8 @@
return;
Flush();
context_.chunk_reader->NotifyEndOfFile();
+ // NotifyEndOfFile might have pushed packets to the sorter.
+ Flush();
for (std::unique_ptr<ProtoImporterModule>& module : context_.modules) {
module->NotifyEndOfFile();
}
diff --git a/src/trace_processor/trace_reader_registry.cc b/src/trace_processor/trace_reader_registry.cc
index 09c5c7b..065f6f6 100644
--- a/src/trace_processor/trace_reader_registry.cc
+++ b/src/trace_processor/trace_reader_registry.cc
@@ -30,7 +30,7 @@
switch (type) {
case kGzipTraceType:
case kCtraceTraceType:
- case kAndroidBugreportTraceType:
+ case kZipFile:
return true;
case kNinjaLogTraceType:
diff --git a/src/trace_processor/util/trace_type.cc b/src/trace_processor/util/trace_type.cc
index d265b90..ee5a56a 100644
--- a/src/trace_processor/util/trace_type.cc
+++ b/src/trace_processor/util/trace_type.cc
@@ -58,8 +58,8 @@
return "gzip trace";
case kCtraceTraceType:
return "ctrace trace";
- case kAndroidBugreportTraceType:
- return "Android Bugreport";
+ case kZipFile:
+ return "ZIP file";
case kPerfDataTraceType:
return "perf data";
case kUnknownTraceType:
@@ -124,12 +124,8 @@
if (base::StartsWith(start, "\x0a"))
return kProtoTraceType;
- // Android bugreport.zip
- // TODO(primiano). For now we assume any .zip file is a bugreport. In future,
- // if we want to support different trace formats based on a .zip arachive we
- // will need an extra layer similar to what we did kGzipTraceType.
if (base::StartsWith(start, "PK\x03\x04"))
- return kAndroidBugreportTraceType;
+ return kZipFile;
return kUnknownTraceType;
}
diff --git a/src/trace_processor/util/trace_type.h b/src/trace_processor/util/trace_type.h
index fec7a74..2d40d83 100644
--- a/src/trace_processor/util/trace_type.h
+++ b/src/trace_processor/util/trace_type.h
@@ -31,7 +31,7 @@
kGzipTraceType,
kCtraceTraceType,
kNinjaLogTraceType,
- kAndroidBugreportTraceType,
+ kZipFile,
kPerfDataTraceType,
};
diff --git a/src/trace_redaction/redact_sched_switch.cc b/src/trace_redaction/redact_sched_switch.cc
index dc8898d..d2ce751 100644
--- a/src/trace_redaction/redact_sched_switch.cc
+++ b/src/trace_redaction/redact_sched_switch.cc
@@ -106,7 +106,7 @@
// collection of ftrace event messages) because data in a sched_switch message
// is needed in order to know if the event should be added to the bundle.
-SchedSwitchTransform::~SchedSwitchTransform() = default;
+RedactSchedSwitchHarness::Modifier::~Modifier() = default;
base::Status RedactSchedSwitchHarness::Transform(const Context& context,
std::string* packet) const {
@@ -188,14 +188,29 @@
for (auto field = decoder.ReadField(); field.valid();
field = decoder.ReadField()) {
- if (field.id() == protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber) {
- protos::pbzero::SchedSwitchFtraceEvent::Decoder sched_switch(
- field.as_bytes());
- RETURN_IF_ERROR(TransformFtraceEventSchedSwitch(
- context, ts.as_uint64(), cpu, sched_switch, &scratch_str,
- message->set_sched_switch()));
- } else {
- proto_util::AppendField(field, message);
+ switch (field.id()) {
+ case protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber: {
+ protos::pbzero::SchedSwitchFtraceEvent::Decoder sched_switch(
+ field.as_bytes());
+ RETURN_IF_ERROR(TransformFtraceEventSchedSwitch(
+ context, ts.as_uint64(), cpu, sched_switch, &scratch_str,
+ message->set_sched_switch()));
+ break;
+ }
+
+ case protos::pbzero::FtraceEvent::kSchedWakingFieldNumber: {
+ protos::pbzero::SchedWakingFtraceEvent::Decoder sched_waking(
+ field.as_bytes());
+ RETURN_IF_ERROR(TransformFtraceEventSchedWaking(
+ context, ts.as_uint64(), cpu, sched_waking, &scratch_str,
+ message->set_sched_waking()));
+ break;
+ }
+
+ default: {
+ proto_util::AppendField(field, message);
+ break;
+ }
}
}
@@ -209,6 +224,10 @@
protos::pbzero::SchedSwitchFtraceEvent::Decoder& sched_switch,
std::string* scratch_str,
protos::pbzero::SchedSwitchFtraceEvent* message) const {
+ PERFETTO_DCHECK(modifier_);
+ PERFETTO_DCHECK(scratch_str);
+ PERFETTO_DCHECK(message);
+
auto has_fields = {
sched_switch.has_prev_comm(), sched_switch.has_prev_pid(),
sched_switch.has_prev_prio(), sched_switch.has_prev_state(),
@@ -233,10 +252,7 @@
scratch_str->assign(prev_comm.data, prev_comm.size);
- for (const auto& transform : transforms_) {
- RETURN_IF_ERROR(
- transform->Transform(context, ts, cpu, &prev_pid, scratch_str));
- }
+ RETURN_IF_ERROR(modifier_->Modify(context, ts, cpu, &prev_pid, scratch_str));
message->set_prev_comm(*scratch_str); // FieldNumber = 1
message->set_prev_pid(prev_pid); // FieldNumber = 2
@@ -245,10 +261,7 @@
scratch_str->assign(next_comm.data, next_comm.size);
- for (const auto& transform : transforms_) {
- RETURN_IF_ERROR(
- transform->Transform(context, ts, cpu, &next_pid, scratch_str));
- }
+ RETURN_IF_ERROR(modifier_->Modify(context, ts, cpu, &next_pid, scratch_str));
message->set_next_comm(*scratch_str); // FieldNumber = 5
message->set_next_pid(next_pid); // FieldNumber = 6
@@ -257,6 +270,47 @@
return base::OkStatus();
}
+base::Status RedactSchedSwitchHarness::TransformFtraceEventSchedWaking(
+ const Context& context,
+ uint64_t ts,
+ int32_t cpu,
+ protos::pbzero::SchedWakingFtraceEvent::Decoder& sched_waking,
+ std::string* scratch_str,
+ protos::pbzero::SchedWakingFtraceEvent* message) const {
+ PERFETTO_DCHECK(modifier_);
+ PERFETTO_DCHECK(scratch_str);
+ PERFETTO_DCHECK(message);
+
+ auto has_fields = {sched_waking.has_comm(), sched_waking.has_pid(),
+ sched_waking.has_prio(), sched_waking.has_success(),
+ sched_waking.has_target_cpu()};
+
+ if (!std::all_of(has_fields.begin(), has_fields.end(), IsTrue)) {
+ return base::ErrStatus(
+ "RedactSchedSwitchHarness: missing required SchedWakingFtraceEvent "
+ "field.");
+ }
+
+ auto pid = sched_waking.pid();
+ auto comm = sched_waking.comm();
+
+ // There are 5 values in a sched switch message. Since 2 of the 5 can be
+ // replaced, it is easier/cleaner to go value-by-value. Go in proto-defined
+ // order.
+
+ scratch_str->assign(comm.data, comm.size);
+
+ RETURN_IF_ERROR(modifier_->Modify(context, ts, cpu, &pid, scratch_str));
+
+ message->set_comm(*scratch_str); // FieldNumber = 1
+ message->set_pid(pid); // FieldNumber = 2
+ message->set_prio(sched_waking.prio()); // FieldNumber = 3
+ message->set_success(sched_waking.success()); // FieldNumber = 4
+ message->set_target_cpu(sched_waking.target_cpu()); // FieldNumber = 5
+
+ return base::OkStatus();
+}
+
base::Status RedactSchedSwitchHarness::TransformCompSched(
const Context& context,
int32_t cpu,
@@ -314,6 +368,9 @@
protos::pbzero::FtraceEventBundle::CompactSched::Decoder& comp_sched,
InternTable* intern_table,
protos::pbzero::FtraceEventBundle::CompactSched* message) const {
+ PERFETTO_DCHECK(modifier_);
+ PERFETTO_DCHECK(message);
+
auto has_fields = {
comp_sched.has_intern_table(),
comp_sched.has_switch_timestamp(),
@@ -359,9 +416,7 @@
scratch_str.assign(comm);
- for (const auto& transform : transforms_) {
- transform->Transform(context, ts, cpu, &pid, &scratch_str);
- }
+ RETURN_IF_ERROR(modifier_->Modify(context, ts, cpu, &pid, &scratch_str));
auto found = intern_table->Push(scratch_str.data(), scratch_str.size());
@@ -429,11 +484,11 @@
// Switch event transformation: Clear the comm value if the thread/process is
// not part of the target packet.
-base::Status ClearComms::Transform(const Context& context,
- uint64_t ts,
- int32_t,
- int32_t* pid,
- std::string* comm) const {
+base::Status ClearComms::Modify(const Context& context,
+ uint64_t ts,
+ int32_t,
+ int32_t* pid,
+ std::string* comm) const {
PERFETTO_DCHECK(pid);
PERFETTO_DCHECK(comm);
diff --git a/src/trace_redaction/redact_sched_switch.h b/src/trace_redaction/redact_sched_switch.h
index 36d6762..906eb11 100644
--- a/src/trace_redaction/redact_sched_switch.h
+++ b/src/trace_redaction/redact_sched_switch.h
@@ -44,25 +44,28 @@
std::vector<std::string_view> interned_comms_;
};
-class SchedSwitchTransform {
- public:
- virtual ~SchedSwitchTransform();
- virtual base::Status Transform(const Context& context,
- uint64_t ts,
- int32_t cpu,
- int32_t* pid,
- std::string* comm) const = 0;
-};
-
-// Goes through all sched switch events are modifies them.
+// TODO(vaage): Rename this class. When it was first created, it only handled
+// switch events, so having "switch" in the name sense. Now that it is
+// expanding to include waking events, a more general name is needed (e.g.
+// scheduling covers both switch and waking events).
class RedactSchedSwitchHarness : public TransformPrimitive {
public:
+ class Modifier {
+ public:
+ virtual ~Modifier();
+ virtual base::Status Modify(const Context& context,
+ uint64_t ts,
+ int32_t cpu,
+ int32_t* pid,
+ std::string* comm) const = 0;
+ };
+
base::Status Transform(const Context& context,
std::string* packet) const override;
template <class Transform>
void emplace_transform() {
- transforms_.emplace_back(new Transform());
+ modifier_ = std::make_unique<Transform>();
}
private:
@@ -86,6 +89,14 @@
std::string* scratch_str,
protos::pbzero::SchedSwitchFtraceEvent* message) const;
+ base::Status TransformFtraceEventSchedWaking(
+ const Context& context,
+ uint64_t ts,
+ int32_t cpu,
+ protos::pbzero::SchedWakingFtraceEvent::Decoder& sched_waking,
+ std::string* scratch_str,
+ protos::pbzero::SchedWakingFtraceEvent* message) const;
+
base::Status TransformCompSched(
const Context& context,
int32_t cpu,
@@ -99,15 +110,15 @@
InternTable* intern_table,
protos::pbzero::FtraceEventBundle::CompactSched* message) const;
- std::vector<std::unique_ptr<SchedSwitchTransform>> transforms_;
+ std::unique_ptr<Modifier> modifier_;
};
-class ClearComms : public SchedSwitchTransform {
- base::Status Transform(const Context& context,
- uint64_t ts,
- int32_t cpu,
- int32_t* pid,
- std::string* comm) const override;
+class ClearComms : public RedactSchedSwitchHarness::Modifier {
+ base::Status Modify(const Context& context,
+ uint64_t ts,
+ int32_t cpu,
+ int32_t* pid,
+ std::string* comm) const override;
};
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/redact_sched_switch_unittest.cc b/src/trace_redaction/redact_sched_switch_unittest.cc
index e2374d1..cd05794 100644
--- a/src/trace_redaction/redact_sched_switch_unittest.cc
+++ b/src/trace_redaction/redact_sched_switch_unittest.cc
@@ -35,25 +35,33 @@
constexpr int32_t kNoParent = 10;
constexpr int32_t kPidA = 11;
constexpr int32_t kPidB = 12;
+constexpr int32_t kPidC = 13;
constexpr int32_t kCpuA = 0;
+constexpr int32_t kCpuB = 1;
+constexpr int32_t kCpuC = 2;
constexpr uint64_t kFullStep = 1000;
constexpr uint64_t kTimeA = 0;
constexpr uint64_t kTimeB = kFullStep;
+constexpr uint64_t kTimeC = kFullStep * 2;
constexpr auto kCommA = "comm-a";
constexpr auto kCommB = "comm-b";
+constexpr auto kCommC = "comm-c";
constexpr auto kCommNone = "";
-class NegatePid : public SchedSwitchTransform {
+class ChangePidToMax : public RedactSchedSwitchHarness::Modifier {
public:
- base::Status Transform(const Context&,
- uint64_t,
- int32_t,
- int32_t* pid,
- std::string*) const override {
- *pid = -(*pid);
+ base::Status Modify(const Context& context,
+ uint64_t ts,
+ int32_t,
+ int32_t* pid,
+ std::string*) const override {
+ if (!context.timeline->PidConnectsToUid(ts, *pid, *context.package_uid)) {
+ *pid = std::numeric_limits<int32_t>::max();
+ }
+
return base::OkStatus();
}
};
@@ -326,12 +334,13 @@
AddSwitchEvent(kTimeA, kPidA, 0, 0, kCommIndexA);
AddSwitchEvent(kTimeB, kPidB, 0, 0, kCommIndexB);
- context_.package_uid = kUidA; // This value is not needed to use NegatePid.
+ // Because the target is package A, PidA should be remain. PidB should change.
+ context_.package_uid = kUidA;
auto packet_buffer = packet_.SerializeAsString();
RedactSchedSwitchHarness redact;
- redact.emplace_transform<NegatePid>();
+ redact.emplace_transform<ChangePidToMax>();
ASSERT_OK(redact.Transform(context_, &packet_buffer));
@@ -347,8 +356,173 @@
ASSERT_EQ(compact_sched.intern_table_size(), 2);
ASSERT_EQ(compact_sched.switch_next_pid_size(), 2);
- ASSERT_EQ(compact_sched.switch_next_pid().at(0), -kPidA);
- ASSERT_EQ(compact_sched.switch_next_pid().at(1), -kPidB);
+ ASSERT_EQ(compact_sched.switch_next_pid().at(0), kPidA);
+ ASSERT_NE(compact_sched.switch_next_pid().at(1), kPidB);
+}
+
+class RedactSchedWakingFtraceEventTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ // Create a packet where two pids are swapping back-and-forth.
+ auto* bundle = packet_.mutable_ftrace_events();
+ bundle->set_cpu(kCpuA);
+
+ // Pid A wakes up Pid B at time Time B
+ {
+ auto* event = bundle->add_event();
+
+ event->set_timestamp(kTimeB);
+ event->set_pid(kPidA);
+
+ auto* sched_waking = event->mutable_sched_waking();
+ sched_waking->set_comm(kCommB);
+ sched_waking->set_pid(kPidB);
+ sched_waking->set_prio(0);
+ sched_waking->set_success(true);
+ sched_waking->set_target_cpu(kCpuB);
+ }
+
+ // Pid A wakes up Pid C at time Time C.
+ {
+ auto* event = bundle->add_event();
+
+ event->set_timestamp(kTimeC);
+ event->set_pid(kPidA);
+
+ auto* sched_waking = event->mutable_sched_waking();
+ sched_waking->set_comm(kCommC);
+ sched_waking->set_pid(kPidC);
+ sched_waking->set_prio(0);
+ sched_waking->set_success(true);
+ sched_waking->set_target_cpu(kCpuC);
+ }
+
+ // PID A and PID B need to be attached to different packages (UID) so that
+ // its possible to include one but not the other.
+ context_.timeline = std::make_unique<ProcessThreadTimeline>();
+ context_.timeline->Append(
+ ProcessThreadTimeline::Event::Open(kTimeA, kPidA, kNoParent, kUidA));
+ context_.timeline->Append(
+ ProcessThreadTimeline::Event::Open(kTimeA, kPidB, kNoParent, kUidB));
+ context_.timeline->Append(
+ ProcessThreadTimeline::Event::Open(kTimeA, kPidC, kNoParent, kUidC));
+ context_.timeline->Sort();
+ }
+
+ protos::gen::TracePacket packet_;
+ Context context_;
+};
+
+TEST_F(RedactSchedWakingFtraceEventTest, WakeeKeepsCommWhenConnectedToPackage) {
+ RedactSchedSwitchHarness redact;
+ redact.emplace_transform<ClearComms>();
+
+ context_.package_uid = kUidB;
+
+ auto packet_buffer = packet_.SerializeAsString();
+
+ ASSERT_OK(redact.Transform(context_, &packet_buffer));
+
+ protos::gen::TracePacket packet;
+ ASSERT_TRUE(packet.ParseFromString(packet_buffer));
+
+ const auto& bundle = packet.ftrace_events();
+ const auto& events = bundle.event();
+
+ ASSERT_EQ(events.size(), 2u);
+
+ ASSERT_EQ(events.front().sched_waking().comm(), kCommB);
+ ASSERT_EQ(events.back().sched_waking().comm(), kCommNone);
+}
+
+TEST_F(RedactSchedWakingFtraceEventTest,
+ WakeeLosesCommWhenNotConnectedToPackage) {
+ RedactSchedSwitchHarness redact;
+ redact.emplace_transform<ClearComms>();
+
+ context_.package_uid = kUidA;
+
+ auto packet_buffer = packet_.SerializeAsString();
+
+ ASSERT_OK(redact.Transform(context_, &packet_buffer));
+
+ protos::gen::TracePacket packet;
+ ASSERT_TRUE(packet.ParseFromString(packet_buffer));
+
+ const auto& bundle = packet.ftrace_events();
+ const auto& events = bundle.event();
+
+ ASSERT_EQ(events.size(), 2u);
+
+ ASSERT_EQ(events.front().sched_waking().comm(), kCommNone);
+ ASSERT_EQ(events.back().sched_waking().comm(), kCommNone);
+}
+
+TEST_F(RedactSchedWakingFtraceEventTest, WakeeKeepsPidWhenConnectedToPackage) {
+ RedactSchedSwitchHarness redact;
+ redact.emplace_transform<ChangePidToMax>();
+
+ context_.package_uid = kUidB;
+
+ auto packet_buffer = packet_.SerializeAsString();
+
+ ASSERT_OK(redact.Transform(context_, &packet_buffer));
+
+ protos::gen::TracePacket packet;
+ ASSERT_TRUE(packet.ParseFromString(packet_buffer));
+
+ const auto& bundle = packet.ftrace_events();
+ const auto& events = bundle.event();
+
+ ASSERT_EQ(events.size(), 2u);
+
+ ASSERT_EQ(events.front().sched_waking().pid(), kPidB);
+ ASSERT_NE(events.back().sched_waking().pid(), kPidC);
+}
+
+TEST_F(RedactSchedWakingFtraceEventTest,
+ WakeeLosesPidWhenNotConnectedToPackage) {
+ RedactSchedSwitchHarness redact;
+ redact.emplace_transform<ChangePidToMax>();
+
+ context_.package_uid = kUidA;
+
+ auto packet_buffer = packet_.SerializeAsString();
+
+ ASSERT_OK(redact.Transform(context_, &packet_buffer));
+
+ protos::gen::TracePacket packet;
+ ASSERT_TRUE(packet.ParseFromString(packet_buffer));
+
+ const auto& bundle = packet.ftrace_events();
+ const auto& events = bundle.event();
+
+ ASSERT_EQ(events.size(), 2u);
+
+ ASSERT_NE(events.front().sched_waking().pid(), kPidB);
+ ASSERT_NE(events.back().sched_waking().pid(), kPidC);
+}
+
+TEST_F(RedactSchedWakingFtraceEventTest, WakerPidIsLeftUnaffected) {
+ RedactSchedSwitchHarness redact;
+ redact.emplace_transform<ChangePidToMax>();
+
+ context_.package_uid = kUidB;
+
+ auto packet_buffer = packet_.SerializeAsString();
+
+ ASSERT_OK(redact.Transform(context_, &packet_buffer));
+
+ protos::gen::TracePacket packet;
+ ASSERT_TRUE(packet.ParseFromString(packet_buffer));
+
+ const auto& bundle = packet.ftrace_events();
+ const auto& events = bundle.event();
+
+ ASSERT_EQ(events.size(), 2u);
+
+ ASSERT_EQ(events.front().pid(), static_cast<uint32_t>(kPidA));
+ ASSERT_EQ(events.back().pid(), static_cast<uint32_t>(kPidA));
}
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/redact_task_newtask.cc b/src/trace_redaction/redact_task_newtask.cc
index 7a0fbeb..0286df2 100644
--- a/src/trace_redaction/redact_task_newtask.cc
+++ b/src/trace_redaction/redact_task_newtask.cc
@@ -46,7 +46,7 @@
const protos::pbzero::FtraceEventBundle::Decoder& bundle,
protozero::ProtoDecoder& event,
protos::pbzero::FtraceEvent* event_message) const {
- PERFETTO_DCHECK(transform_);
+ PERFETTO_DCHECK(modifier_);
if (!context.package_uid.has_value()) {
return base::ErrStatus("RedactTaskNewTask: missing package uid");
@@ -84,8 +84,8 @@
auto cpu = static_cast<int32_t>(bundle.cpu());
- RETURN_IF_ERROR(transform_->Transform(context, timestamp_field.as_uint64(),
- cpu, &pid, &comm));
+ RETURN_IF_ERROR(modifier_->Modify(context, timestamp_field.as_uint64(), cpu,
+ &pid, &comm));
auto* new_task_message = event_message->set_task_newtask();
new_task_message->set_pid(pid);
diff --git a/src/trace_redaction/redact_task_newtask.h b/src/trace_redaction/redact_task_newtask.h
index 5b589fc..61c8b52 100644
--- a/src/trace_redaction/redact_task_newtask.h
+++ b/src/trace_redaction/redact_task_newtask.h
@@ -36,13 +36,13 @@
protozero::ProtoDecoder& event,
protos::pbzero::FtraceEvent* event_message) const override;
- template <class Transform>
+ template <class Modifier>
void emplace_back() {
- transform_ = std::make_unique<Transform>();
+ modifier_ = std::make_unique<Modifier>();
}
public:
- std::unique_ptr<SchedSwitchTransform> transform_;
+ std::unique_ptr<RedactSchedSwitchHarness::Modifier> modifier_;
};
} // namespace perfetto::trace_redaction
diff --git a/src/traceconv/symbolize_profile.cc b/src/traceconv/symbolize_profile.cc
index 00cc7b9..4efaa10 100644
--- a/src/traceconv/symbolize_profile.cc
+++ b/src/traceconv/symbolize_profile.cc
@@ -55,12 +55,12 @@
PERFETTO_FATAL("Failed to read trace.");
tp->Flush();
+ tp->NotifyEndOfFile();
SymbolizeDatabase(
tp.get(), symbolizer.get(),
[output](const std::string& trace_proto) { *output << trace_proto; });
- tp->NotifyEndOfFile();
return 0;
}
diff --git a/test/.gitignore b/test/.gitignore
index 369c917..47a77bd 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -22,3 +22,7 @@
!/data/simpleperf
/data/simpleperf/*
!/data/simpleperf/*.sha256
+
+!/data/zip
+/data/zip/*
+!/data/zip/*.sha256
\ No newline at end of file
diff --git a/test/data/zip/perf_track_sym.zip.sha256 b/test/data/zip/perf_track_sym.zip.sha256
new file mode 100644
index 0000000..53dfcb3
--- /dev/null
+++ b/test/data/zip/perf_track_sym.zip.sha256
@@ -0,0 +1 @@
+146b1d8f48743323e91fd63d6ab5a1f6d8fcca8c5ea8455ebe8f087667a23c3f
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 33ea8c2..e70dfa3 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -96,6 +96,7 @@
from diff_tests.parser.track_event.tests import TrackEvent
from diff_tests.parser.translated_args.tests import TranslatedArgs
from diff_tests.parser.ufs.tests import Ufs
+from diff_tests.parser.zip.tests import Zip
from diff_tests.stdlib.android.frames_tests import Frames
from diff_tests.stdlib.android.startups_tests import Startups
from diff_tests.stdlib.android.tests import AndroidStdlib
@@ -220,6 +221,7 @@
*FtraceCrop(index_path, 'parser/ftrace', 'FtraceCrop').fetch(),
*ParsingTracedStats(index_path, 'parser/parsing',
'ParsingTracedStats').fetch(),
+ *Zip(index_path, 'parser/zip', 'Zip').fetch(),
]
metrics_tests = [
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup.out b/test/trace_processor/diff_tests/metrics/startup/android_startup.out
index 9dfc289..9928efb 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup.out
@@ -67,10 +67,11 @@
installd_dur_ns: 0
dex2oat_dur_ns: 0
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_TIME_SPENT_IN_RUNNABLE
reason: "Main Thread - Time spent in Runnable state"
- details: " target 15% actual 74.07% [ longest_chunk: start_s 3.0e-08 dur_ms 8.0e-0 thread_id 3 thread_name com.google.android.calendar ] [ extra_info: launches_dur_ms 0.0001 runnable_dur_ms 8.0e-0 R_sum_dur_ms 8.0e-0 R+(Preempted)_sum_dur_ms 0.0 ]"
- }
+ launch_dur: 108
+ }
startup_type: "warm"
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out
index 9b6e742..30a1b23 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out
@@ -134,14 +134,20 @@
slow_start_reason: "GC Activity"
slow_start_reason: "Main Thread - Time spent in OpenDexFilesFromOat*"
slow_start_reason: "Main Thread - Binder transactions blocked"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: GC_ACTIVITY
reason: "GC Activity"
+ launch_dur: 999999900
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_TIME_SPENT_IN_OPEN_DEX_FILES_FROM_OAT
reason: "Main Thread - Time spent in OpenDexFilesFromOat*"
+ launch_dur: 999999900
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_BINDER_TRANSCATIONS_BLOCKED
reason: "Main Thread - Binder transactions blocked"
+ launch_dur: 999999900
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out
index 3e8ff27..cd305f1 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out
@@ -98,15 +98,20 @@
slow_start_reason: "GC Activity"
slow_start_reason: "JIT Activity"
slow_start_reason: "JIT compiled methods"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: GC_ACTIVITY
reason: "GC Activity"
+ launch_dur: 999999900000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: JIT_ACTIVITY
reason: "JIT Activity"
- details: " target 100ms actual 20000.ms [ longest_chunk: start_s 154.99 dur_ms 10000. thread_id 4 thread_name Jit thread pool ]"
+ launch_dur: 999999900000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: JIT_COMPILED_METHODS
reason: "JIT compiled methods"
+ launch_dur: 999999900000000000
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out
index f40578d..e91c756 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out
@@ -119,26 +119,35 @@
slow_start_reason: "Time spent in bindApplication"
slow_start_reason: "Time spent in view inflation"
slow_start_reason: "Time spent in ResourcesManager#getResources"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: NO_BASELINE_OR_CLOUD_PROFILES
reason: "No baseline or cloud profiles"
- details: " target FALSE actual TRUE [ longest_chunk: start_s 154.0 dur_ms 1000.0 thread_id -1 thread_name com.google.android.calendar ] [ extra_info: slice_name location=/system/framework/oat/arm/com.google.android.calendar.odex status=up-to-date filter=speed reason=install-dm ]"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: RUN_FROM_APK
reason: "Optimized artifacts missing, run from apk"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: TIME_SPENT_IN_BIND_APPLICATION
reason: "Time spent in bindApplication"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: TIME_SPENT_IN_VIEW_INFLATION
reason: "Time spent in view inflation"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: TIME_SPENT_IN_RESOURCES_MANAGER_GET_RESOURCES
reason: "Time spent in ResourcesManager#getResources"
- details: " target 130ms actual 1000.0ms [ longest_chunk: start_s 138.0 dur_ms 1000.0 thread_id 3 thread_name com.google.android.calendar ]"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: POTENTIAL_CPU_CONTENTION_WITH_ANOTHER_PROCESS
reason: "Potential CPU contention with another process"
- details: " target 100ms actual 5000.0ms most_active_process_for_launch init [ longest_chunk: start_s 155.0 dur_ms 5000.0 thread_id 3 thread_name com.google.android.calendar ] [ extra_info: runnable_dur_ms 5000.0 R_sum_dur_ms 5000.0 R+(Preempted)_sum_dur 0 ]"
+ launch_dur: 108000000000
}
startup_type: "cold"
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out
index 891060b..bb11660 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out
@@ -118,22 +118,30 @@
slow_start_reason: "Time spent in bindApplication"
slow_start_reason: "Time spent in view inflation"
slow_start_reason: "Time spent in ResourcesManager#getResources"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: RUN_FROM_APK
reason: "Optimized artifacts missing, run from apk"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: TIME_SPENT_IN_BIND_APPLICATION
reason: "Time spent in bindApplication"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: TIME_SPENT_IN_VIEW_INFLATION
reason: "Time spent in view inflation"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: TIME_SPENT_IN_RESOURCES_MANAGER_GET_RESOURCES
reason: "Time spent in ResourcesManager#getResources"
- details: " target 130ms actual 5000.0ms [ longest_chunk: start_s 137.0 dur_ms 5000.0 thread_id 3 thread_name com.google.android.calendar ]"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: POTENTIAL_CPU_CONTENTION_WITH_ANOTHER_PROCESS
reason: "Potential CPU contention with another process"
- details: " target 100ms actual 5000.0ms most_active_process_for_launch init [ longest_chunk: start_s 155.0 dur_ms 5000.0 thread_id 3 thread_name com.google.android.calendar ] [ extra_info: runnable_dur_ms 5000.0 R_sum_dur_ms 5000.0 R+(Preempted)_sum_dur 0 ]"
+ launch_dur: 108000000000
}
startup_type: "cold"
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out
index 02e142c..7f7d68c 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out
@@ -31,11 +31,15 @@
}
slow_start_reason: "Broadcast dispatched count"
slow_start_reason: "Broadcast received count"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: BROADCAST_DISPATCHED_COUNT
reason: "Broadcast dispatched count"
+ launch_dur: 100
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: BROADCAST_RECEIVED_COUNT
reason: "Broadcast received count"
+ launch_dur: 100
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat.out
index 1fd7f2e..488e597 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat.out
@@ -62,8 +62,10 @@
dex2oat_dur_ns: 5
}
slow_start_reason: "dex2oat running during launch"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: DEX2OAT_RUNNING
reason: "dex2oat running during launch"
+ launch_dur: 100
}
}
startup {
@@ -98,8 +100,10 @@
dex2oat_dur_ns: 0
}
slow_start_reason: "installd running during launch"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: INSTALLD_RUNNING
reason: "installd running during launch"
+ launch_dur: 100
}
}
startup {
@@ -136,11 +140,15 @@
}
slow_start_reason: "dex2oat running during launch"
slow_start_reason: "installd running during launch"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: DEX2OAT_RUNNING
reason: "dex2oat running during launch"
+ launch_dur: 100
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: INSTALLD_RUNNING
reason: "installd running during launch"
+ launch_dur: 100
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat_slow.out
index 91e0d5a..2cc2b16 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_installd_dex2oat_slow.out
@@ -62,8 +62,10 @@
dex2oat_dur_ns: 5000000000
}
slow_start_reason: "dex2oat running during launch"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: DEX2OAT_RUNNING
reason: "dex2oat running during launch"
+ launch_dur: 100000000000
}
}
startup {
@@ -101,14 +103,20 @@
slow_start_reason: "dex2oat running during launch"
slow_start_reason: "installd running during launch"
slow_start_reason: "Startup running concurrent to launch"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: DEX2OAT_RUNNING
reason: "dex2oat running during launch"
+ launch_dur: 250000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: INSTALLD_RUNNING
reason: "installd running during launch"
+ launch_dur: 250000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: STARTUP_RUNNING_CONCURRENT
reason: "Startup running concurrent to launch"
+ launch_dur: 250000000000
}
startup_concurrent_to_launch: "com.google.android.gm"
}
@@ -145,14 +153,20 @@
slow_start_reason: "dex2oat running during launch"
slow_start_reason: "installd running during launch"
slow_start_reason: "Startup running concurrent to launch"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: DEX2OAT_RUNNING
reason: "dex2oat running during launch"
+ launch_dur: 100000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: INSTALLD_RUNNING
reason: "installd running during launch"
+ launch_dur: 100000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: STARTUP_RUNNING_CONCURRENT
reason: "Startup running concurrent to launch"
+ launch_dur: 100000000000
}
startup_concurrent_to_launch: "com.google.android.deskclock"
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out
index 3e5f6a6..05fc7d1 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out
@@ -72,14 +72,20 @@
slow_start_reason: "Time spent in bindApplication"
slow_start_reason: "Main Thread - Lock contention"
slow_start_reason: "Main Thread - Monitor contention"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: TIME_SPENT_IN_BIND_APPLICATION
reason: "Time spent in bindApplication"
+ launch_dur: 100000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_LOCK_CONTENTION
reason: "Main Thread - Lock contention"
+ launch_dur: 100000000000
}
- slow_start_reason_detailed {
- reason: "Main Thread - Monitor contention"
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_MONITOR_CONTENTION
+ reason: "Main Thread - Monitor contention"
+ launch_dur: 100000000000
}
startup_type: "cold"
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_minsdk33.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_minsdk33.out
index e56ae6f..1ee29d4 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_minsdk33.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_minsdk33.out
@@ -30,8 +30,10 @@
dex2oat_dur_ns: 0
}
slow_start_reason: "App in debuggable mode"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: APP_IN_DEBUGGABLE_MODE
reason: "App in debuggable mode"
+ launch_dur: 100
}
}
startup {
@@ -86,8 +88,10 @@
}
startup_type: "hot"
slow_start_reason: "App in debuggable mode"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: APP_IN_DEBUGGABLE_MODE
reason: "App in debuggable mode"
+ launch_dur: 10
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out
index fb10a32..efd407a 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out
@@ -66,9 +66,10 @@
dex2oat_dur_ns: 0
}
startup_type: "cold"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_TIME_SPENT_IN_RUNNABLE
reason: "Main Thread - Time spent in Runnable state"
- details: " target 15% actual 57.14% [ longest_chunk: start_s 3.0e-09 dur_ms 4.0e-0 thread_id 3 thread_name com.google.android.calendar ] [ extra_info: launches_dur_ms 7.0e-0 runnable_dur_ms 4.0e-0 R_sum_dur_ms 4.0e-0 R+(Preempted)_sum_dur_ms 0.0 ]"
+ launch_dur: 7
}
}
startup {
@@ -139,9 +140,10 @@
dex2oat_dur_ns: 0
}
startup_type: "cold"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_TIME_SPENT_IN_RUNNABLE
reason: "Main Thread - Time spent in Runnable state"
- details: " target 15% actual 57.14% [ longest_chunk: start_s 1.03e-07 dur_ms 4.0e-0 thread_id 4 thread_name com.google.android.calendar ] [ extra_info: launches_dur_ms 7.0e-0 runnable_dur_ms 4.0e-0 R_sum_dur_ms 4.0e-0 R+(Preempted)_sum_dur_ms 0.0 ]"
+ launch_dur: 7
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out
index 4cdc5e9..1b038e6 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out
@@ -70,19 +70,25 @@
startup_type: "warm"
slow_start_reason: "Main Thread - Time spent in interruptible sleep state"
slow_start_reason: "Main Thread - Time spent in Blocking I/O"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_TIME_SPENT_IN_RUNNABLE
reason: "Main Thread - Time spent in Runnable state"
- details: " target 15% actual 74.07% [ longest_chunk: start_s 30.0 dur_ms 80000. thread_id 3 thread_name com.google.android.calendar ] [ extra_info: launches_dur_ms 108000 runnable_dur_ms 80000. R_sum_dur_ms 80000. R+(Preempted)_sum_dur_ms 0.0 ]"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_TIME_SPENT_IN_INTERRUPTIBLE_SLEEP
reason: "Main Thread - Time spent in interruptible sleep state"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: MAIN_THREAD_TIME_SPENT_IN_BLOCKING_IO
reason: "Main Thread - Time spent in Blocking I/O"
+ launch_dur: 108000000000
}
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: POTENTIAL_CPU_CONTENTION_WITH_ANOTHER_PROCESS
reason: "Potential CPU contention with another process"
- details: " target 100ms actual 80000.ms most_active_process_for_launch init [ longest_chunk: start_s 30.0 dur_ms 80000. thread_id 3 thread_name com.google.android.calendar ] [ extra_info: runnable_dur_ms 80000. R_sum_dur_ms 80000. R+(Preempted)_sum_dur 0 ]"
+ launch_dur: 108000000000
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out
index 9686456..cf87a52 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out
@@ -30,8 +30,10 @@
dex2oat_dur_ns: 0
}
slow_start_reason: "Unlock running during launch"
- slow_start_reason_detailed {
+ slow_start_reason_with_details {
+ reason_id: UNLOCK_RUNNING
reason: "Unlock running during launch"
+ launch_dur: 100
}
}
}
diff --git a/test/trace_processor/diff_tests/parser/simpleperf/clocks_align_test.sql b/test/trace_processor/diff_tests/parser/simpleperf/clocks_align_test.sql
new file mode 100644
index 0000000..f5a65ee
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/simpleperf/clocks_align_test.sql
@@ -0,0 +1,99 @@
+--
+-- Copyright 2024 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
+--
+-- https://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.
+--
+
+CREATE PERFETTO VIEW perf_sample_in(ts INT, dur INT)
+AS
+SELECT ts, 0 AS dur FROM perf_sample;
+
+CREATE VIRTUAL TABLE span
+USING
+ SPAN_JOIN(perf_sample_in, slice PARTITIONED depth);
+
+CREATE PERFETTO TABLE slice_stack
+AS
+WITH
+ tmp AS (
+ SELECT
+ ts,
+ parent_stack_id,
+ string_AGG(IIF(name = 'Main loop', 'main', name), ',')
+ OVER (
+ PARTITION BY ts
+ ORDER BY depth ASC
+ RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ ) AS stack
+ FROM span
+ )
+SELECT ts, stack FROM tmp WHERE parent_stack_id = 0 ORDER BY TS ASC;
+
+CREATE PERFETTO TABLE perf_stack
+AS
+WITH
+ symbol AS (
+ SELECT
+ id,
+ symbol_set_id,
+ replace(replace(name, '(anonymous namespace)::', ''), '()', '') AS name
+ FROM stack_profile_symbol
+ ),
+ symbol_agg AS (
+ SELECT
+ id,
+ symbol_set_id,
+ string_agg(name, ',')
+ OVER (
+ PARTITION BY symbol_set_id
+ ORDER BY id DESC
+ RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ ) AS name
+ FROM symbol
+ WHERE name IN ('main', 'A', 'B', 'C', 'D', 'E')
+ ),
+ inline AS (
+ SELECT symbol_set_id, name FROM symbol_agg WHERE id = symbol_set_id
+ ),
+ frame AS (
+ SELECT f.id AS frame_id, i.name
+ FROM STACK_PROFILE_FRAME f, inline i
+ USING (symbol_set_id)
+ ),
+ child AS (
+ SELECT
+ s.ts,
+ spc.id,
+ spc.parent_id,
+ name
+ FROM perf_sample s, stack_profile_callsite spc
+ ON (s.callsite_id = spc.id),
+ frame USING (frame_id)
+ UNION ALL
+ SELECT
+ child.ts,
+ parent.id,
+ parent.parent_id,
+ COALESCE(f.name || ',', '') || child.name AS name
+ FROM child, stack_profile_callsite parent
+ ON (child.parent_id = parent.id)
+ LEFT JOIN frame f
+ USING (frame_id)
+ )
+SELECT ts, name AS stack FROM child WHERE parent_id IS NULL ORDER BY ts ASC;
+
+SELECT COUNT(*) AS misaligned_count
+FROM slice_stack s
+FULL JOIN perf_stack p
+ USING (ts)
+WHERE s.stack <> p.stack;
diff --git a/test/trace_processor/diff_tests/parser/simpleperf/tests.py b/test/trace_processor/diff_tests/parser/simpleperf/tests.py
index c98239d..4f5a22b 100644
--- a/test/trace_processor/diff_tests/parser/simpleperf/tests.py
+++ b/test/trace_processor/diff_tests/parser/simpleperf/tests.py
@@ -142,3 +142,12 @@
"0b12a384a9f4a3f3659b7171ca615dbec3a81f71","/t1"
"0b12a384a9f4a3f3659b7171ca615dbec3a81f71","/t2"
'''))
+
+ def test_clocks_align(self):
+ return DiffTestBlueprint(
+ trace=DataPath('zip/perf_track_sym.zip'),
+ query=Path('clocks_align_test.sql'),
+ out=Csv('''
+ "misaligned_count"
+ 0
+ '''))
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/parser/zip/stacks_test.sql b/test/trace_processor/diff_tests/parser/zip/stacks_test.sql
new file mode 100644
index 0000000..68543f1
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/zip/stacks_test.sql
@@ -0,0 +1,48 @@
+WITH
+ symbol AS (
+ SELECT
+ id,
+ symbol_set_id,
+ replace(replace(name, '(anonymous namespace)::', ''), '()', '') AS name
+ FROM stack_profile_symbol
+ ),
+ symbol_agg AS (
+ SELECT
+ id,
+ symbol_set_id,
+ string_agg(name, ',')
+ OVER (
+ PARTITION BY symbol_set_id
+ ORDER BY id DESC
+ RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+ ) AS name
+ FROM symbol
+ WHERE name IN ('main', 'A', 'B', 'C', 'D', 'E')
+ ),
+ inline AS (
+ SELECT symbol_set_id, name FROM symbol_agg WHERE id = symbol_set_id
+ ),
+ frame AS (
+ SELECT f.id AS frame_id, i.name
+ FROM STACK_PROFILE_FRAME f, inline i
+ USING (symbol_set_id)
+ ),
+ child AS (
+ SELECT
+ spc.id,
+ spc.parent_id,
+ name
+ FROM perf_sample s, stack_profile_callsite spc
+ ON (s.callsite_id = spc.id),
+ frame USING (frame_id)
+ UNION ALL
+ SELECT
+ parent.id,
+ parent.parent_id,
+ COALESCE(f.name || ',', '') || child.name AS name
+ FROM child, stack_profile_callsite parent
+ ON (child.parent_id = parent.id)
+ LEFT JOIN frame f
+ USING (frame_id)
+ )
+SELECT DISTINCT name FROM child WHERE parent_id IS NULL ORDER BY name
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/parser/zip/tests.py b/test/trace_processor/diff_tests/parser/zip/tests.py
new file mode 100644
index 0000000..9827072
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/zip/tests.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+# 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.
+
+from python.generators.diff_tests.testing import Csv, Path, DataPath
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Zip(TestSuite):
+
+ def test_perf_proto_sym(self):
+ return DiffTestBlueprint(
+ trace=DataPath('zip/perf_track_sym.zip'),
+ query=Path('stacks_test.sql'),
+ out=Csv('''
+ "name"
+ "main,A"
+ "main,A,B"
+ "main,A,B,C"
+ "main,A,B,C,D"
+ "main,A,B,C,D,E"
+ "main,A,B,C,E"
+ "main,A,B,D"
+ "main,A,B,D,E"
+ "main,A,B,E"
+ "main,A,C"
+ "main,A,C,D"
+ "main,A,C,D,E"
+ "main,A,C,E"
+ "main,A,D"
+ "main,A,D,E"
+ "main,A,E"
+ "main,B"
+ "main,B,C"
+ "main,B,C,D"
+ "main,B,C,D,E"
+ "main,B,C,E"
+ "main,B,D"
+ "main,B,D,E"
+ "main,B,E"
+ "main,C"
+ "main,C,D"
+ "main,C,D,E"
+ "main,C,E"
+ "main,D"
+ "main,D,E"
+ "main,E"
+ '''))
diff --git a/ui/src/core/default_plugins.ts b/ui/src/core/default_plugins.ts
index 4f5460e..44b4e21 100644
--- a/ui/src/core/default_plugins.ts
+++ b/ui/src/core/default_plugins.ts
@@ -58,4 +58,5 @@
'perfetto.VisualisedArgs',
'org.kernel.LinuxKernelDevices',
'perfetto.TrackUtils',
+ 'com.google.PixelMemory',
];
diff --git a/ui/src/plugins/com.google.PixelMemory/OWNERS b/ui/src/plugins/com.google.PixelMemory/OWNERS
new file mode 100644
index 0000000..249152a
--- /dev/null
+++ b/ui/src/plugins/com.google.PixelMemory/OWNERS
@@ -0,0 +1 @@
+liumartin@google.com
diff --git a/ui/src/plugins/com.google.PixelMemory/index.ts b/ui/src/plugins/com.google.PixelMemory/index.ts
new file mode 100644
index 0000000..c79faf2
--- /dev/null
+++ b/ui/src/plugins/com.google.PixelMemory/index.ts
@@ -0,0 +1,63 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {Plugin, PluginContextTrace, PluginDescriptor} from '../../public';
+
+import {addDebugCounterTrack} from '../../frontend/debug_tracks';
+
+class PixelMemory implements Plugin {
+ async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+ ctx.registerCommand({
+ id: 'dev.perfetto.PixelMemory#ShowTotalMemory',
+ name: 'Add tracks: show a process total memory',
+ callback: async (pid) => {
+ if (pid === undefined) {
+ pid = prompt('Enter a process pid', '');
+ if (pid === null) return;
+ }
+ const RSS_ALL = `
+ INCLUDE PERFETTO MODULE memory.linux.process;
+ INCLUDE PERFETTO MODULE memory.android.gpu;
+
+ DROP TABLE IF EXISTS process_mem_rss_anon_file_shmem_swap_gpu;
+
+ CREATE VIRTUAL TABLE process_mem_rss_anon_file_shmem_swap_gpu
+ USING
+ SPAN_OUTER_JOIN(
+ memory_gpu_per_process PARTITIONED upid, memory_rss_and_swap_per_process PARTITIONED upid);
+ `;
+ await ctx.engine.query(RSS_ALL);
+ await addDebugCounterTrack(
+ {
+ sqlSource: `
+ SELECT
+ ts,
+ COALESCE(rss_and_swap, 0) + COALESCE(gpu_memory, 0) AS value
+ FROM process_mem_rss_anon_file_shmem_swap_gpu
+ WHERE pid = ${pid}
+ `,
+ columns: ['ts', 'value'],
+ },
+ pid + '_rss_anon_file_swap_shmem_gpu',
+ {ts: 'ts', value: 'value'},
+ );
+ },
+ });
+ }
+}
+
+export const plugin: PluginDescriptor = {
+ pluginId: 'com.google.PixelMemory',
+ plugin: PixelMemory,
+};