Merge "Add missing sqlite initialization to trace processor unittests"
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 4939305..cdbdb6a 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -14,6 +14,7 @@
import("../gn/perfetto.gni")
import("../gn/standalone/libc++/libc++.gni")
+import("../gn/standalone/sanitizers/vars.gni")
# We should never get here in embedder builds.
assert(perfetto_build_standalone || is_perfetto_build_generator)
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 6ac8191..6e2afb4 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -285,7 +285,7 @@
}
# Used by fuzzers.
-if (enable_perfetto_fuzzers) {
+if (enable_perfetto_fuzzers && use_libfuzzer) {
group("libfuzzer") {
assert(perfetto_root_path == "//")
public_deps = [
diff --git a/include/perfetto/trace_processor/basic_types.h b/include/perfetto/trace_processor/basic_types.h
index 59fb559..7b29427 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -18,11 +18,14 @@
#define INCLUDE_PERFETTO_TRACE_PROCESSOR_BASIC_TYPES_H_
#include <assert.h>
+#include <math.h>
#include <stdarg.h>
#include <stdint.h>
+#include <functional>
#include <string>
#include "perfetto/base/export.h"
+#include "perfetto/base/logging.h"
namespace perfetto {
namespace trace_processor {
@@ -34,17 +37,68 @@
// Represents the type of the value.
enum Type {
kNull = 0,
- kString,
kLong,
kDouble,
+ kString,
kBytes,
};
+ SqlValue() = default;
+
+ static SqlValue Long(int64_t v) {
+ SqlValue value;
+ value.long_value = v;
+ value.type = Type::kLong;
+ return value;
+ }
+
+ static SqlValue String(const char* v) {
+ SqlValue value;
+ value.string_value = v;
+ value.type = Type::kString;
+ return value;
+ }
+
double AsDouble() {
assert(type == kDouble);
return double_value;
}
+ int Compare(const SqlValue& value) const {
+ // TODO(lalitm): this is almost the same as what SQLite does with the
+ // exception of comparisions between long and double - we choose (for
+ // performance reasons) to omit comparisions between them.
+ if (type != value.type)
+ return type - value.type;
+
+ switch (type) {
+ case Type::kNull:
+ return 0;
+ case Type::kLong:
+ return signbit(long_value - value.long_value);
+ case Type::kDouble:
+ return signbit(double_value - value.double_value);
+ case Type::kString:
+ return strcmp(string_value, value.string_value);
+ case Type::kBytes: {
+ size_t bytes = std::min(bytes_count, value.bytes_count);
+ int ret = memcmp(bytes_value, value.bytes_value, bytes);
+ if (ret != 0)
+ return ret;
+ return signbit(bytes_count - value.bytes_count);
+ }
+ }
+ PERFETTO_FATAL("For GCC");
+ }
+ bool operator==(const SqlValue& value) const { return Compare(value) == 0; }
+ bool operator<(const SqlValue& value) const { return Compare(value) < 0; }
+ bool operator!=(const SqlValue& value) const { return !(*this == value); }
+ bool operator>=(const SqlValue& value) const { return !(*this < value); }
+ bool operator<=(const SqlValue& value) const { return !(value < *this); }
+ bool operator>(const SqlValue& value) const { return value < *this; }
+
+ bool is_null() const { return type == Type::kNull; }
+
// Up to 1 of these fields can be accessed depending on |type|.
union {
// This string will be owned by the iterator that returned it and is valid
diff --git a/infra/oss-fuzz/build_fuzzers b/infra/oss-fuzz/build_fuzzers
index 7275a69..19a36a2 100755
--- a/infra/oss-fuzz/build_fuzzers
+++ b/infra/oss-fuzz/build_fuzzers
@@ -8,8 +8,10 @@
GN_ARGS="is_clang=true is_debug=false is_fuzzer=true use_libfuzzer=false \
link_fuzzer=\"-lFuzzingEngine\" is_asan=true is_hermetic_clang=false \
-use_custom_libcxx=false extra_cflags=\"$CFLAGS\" extra_cxxflags=\"$CXXFLAGS\" \
-extra_ldflags=\"$CXXFLAGS\" is_system_compiler=true"
+use_custom_libcxx=false \
+extra_cflags=\"$CFLAGS -Wno-implicit-int-float-conversion\" \
+extra_cxxflags=\"$CXXFLAGS\" extra_ldflags=\"$CXXFLAGS\" \
+is_system_compiler=true"
OUTDIR=$WORK/build
$SRC/perfetto/tools/gn gen "$OUTDIR" --args="${GN_ARGS}" --check
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 71549cd..78a6142 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -95,7 +95,6 @@
"metadata.h",
"metadata_table.cc",
"metadata_table.h",
- "null_term_string_view.h",
"process_table.cc",
"process_table.h",
"process_tracker.cc",
@@ -136,8 +135,6 @@
"storage_schema.h",
"storage_table.cc",
"storage_table.h",
- "string_pool.cc",
- "string_pool.h",
"syscall_tracker.cc",
"syscall_tracker.h",
"syscalls_aarch32.h",
@@ -172,6 +169,7 @@
]
deps = [
+ ":common",
"../../gn:default_deps",
"../../gn:sqlite",
"../../gn:zlib",
@@ -217,6 +215,21 @@
}
}
+# TODO(lalitm): we need to find a better home for the classes here.
+source_set("common") {
+ sources = [
+ "null_term_string_view.h",
+ "string_pool.cc",
+ "string_pool.h",
+ ]
+
+ deps = [
+ "../../gn:default_deps",
+ "../base",
+ "../protozero",
+ ]
+}
+
executable("trace_processor_shell") {
testonly = true # We need this for proto full.
deps = [
@@ -266,6 +279,7 @@
"trace_sorter_unittest.cc",
]
deps = [
+ ":common",
":lib",
"../../gn:default_deps",
"../../gn:gtest_and_gmock",
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index c835ea2..b6e76cd 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -27,9 +27,11 @@
"table.h",
]
deps = [
+ "../:common",
"../../../gn:default_deps",
"../../../include/perfetto/base",
"../../../include/perfetto/ext/base",
+ "../../../include/perfetto/trace_processor",
]
}
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index 6ab4c00..20350d4 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -21,26 +21,29 @@
namespace perfetto {
namespace trace_processor {
-void Column::FilterInto(FilterOp op, int64_t value, RowMap* iv) const {
+Column::Column(const char* name,
+ ColumnType type,
+ Table* table,
+ uint32_t col_idx,
+ uint32_t row_map_idx)
+ : name_(name),
+ table_(table),
+ string_pool_(table->string_pool_),
+ col_idx_(col_idx),
+ row_map_idx_(row_map_idx),
+ type_(type) {}
+
+void Column::FilterInto(FilterOp op, SqlValue value, RowMap* iv) const {
// Assume op == kEq.
switch (op) {
case FilterOp::kLt:
- iv->RemoveIf([this, value](uint32_t row) {
- auto opt_value = Get(row);
- return !opt_value || opt_value.value() >= value;
- });
+ iv->RemoveIf([this, value](uint32_t row) { return Get(row) >= value; });
break;
case FilterOp::kEq:
- iv->RemoveIf([this, value](uint32_t row) {
- auto opt_value = Get(row);
- return !opt_value || opt_value.value() != value;
- });
+ iv->RemoveIf([this, value](uint32_t row) { return Get(row) != value; });
break;
case FilterOp::kGt:
- iv->RemoveIf([this, value](uint32_t row) {
- auto opt_value = Get(row);
- return !opt_value || opt_value.value() <= value;
- });
+ iv->RemoveIf([this, value](uint32_t row) { return Get(row) <= value; });
break;
}
}
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index 834ba44..8459add 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -21,8 +21,10 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/optional.h"
+#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/db/row_map.h"
#include "src/trace_processor/db/sparse_vector.h"
+#include "src/trace_processor/string_pool.h"
namespace perfetto {
namespace trace_processor {
@@ -38,7 +40,7 @@
struct Constraint {
uint32_t col_idx;
FilterOp op;
- int64_t value;
+ SqlValue value;
};
// Represents an order by operation on a column.
@@ -68,6 +70,19 @@
data_.int64_sv = storage;
}
+ // Create an nullable string Column.
+ // Note: |name| must be a long lived string.
+ // TODO(lalitm): change this to a std::deque instead as StringIds already
+ // have the concept of nullability in them.
+ Column(const char* name,
+ const SparseVector<StringPool::Id>* storage,
+ Table* table,
+ uint32_t col_idx,
+ uint32_t row_map_idx)
+ : Column(name, ColumnType::kString, table, col_idx, row_map_idx) {
+ data_.string_sv = storage;
+ }
+
// Create a Column has the same name and is backed by the same data as
// |column| but is associated to a different table.
Column(const Column& column,
@@ -87,44 +102,64 @@
}
// Gets the value of the Column at the given |row|
- base::Optional<int64_t> Get(uint32_t row) const {
- auto opt_idx = row_map().Get(row);
+ SqlValue Get(uint32_t row) const {
+ auto idx = row_map().Get(row);
switch (type_) {
- case ColumnType::kInt64:
- return data_.int64_sv->Get(opt_idx);
+ case ColumnType::kInt64: {
+ auto opt_value = data_.int64_sv->Get(idx);
+ return opt_value ? SqlValue::Long(*opt_value) : SqlValue();
+ }
+ case ColumnType::kString: {
+ auto opt_id = data_.string_sv->Get(idx);
+ // We DCHECK here because although we are using SparseVector, the null
+ // info is handled by the StringPool rather than by the SparseVector.
+ // The value returned by the SparseVector should always be non-null.
+ // TODO(lalitm): remove this check when we support std::deque<StringId>.
+ PERFETTO_DCHECK(opt_id.has_value());
+ auto str = string_pool_->Get(*opt_id).c_str();
+ return str == nullptr ? SqlValue() : SqlValue::String(str);
+ }
case ColumnType::kId:
- return opt_idx;
+ return SqlValue::Long(idx);
}
PERFETTO_FATAL("For GCC");
}
// Returns the row containing the given value in the Column.
- base::Optional<uint32_t> IndexOf(int64_t value) const {
+ base::Optional<uint32_t> IndexOf(SqlValue value) const {
switch (type_) {
+ // TODO(lalitm): investigate whether we could make this more efficient
+ // by first checking the type of the column and comparing explicitly
+ // based on that type.
case ColumnType::kInt64:
+ case ColumnType::kString: {
for (uint32_t i = 0; i < row_map().size(); i++) {
if (Get(i) == value)
return i;
}
return base::nullopt;
- case ColumnType::kId:
- return row_map().IndexOf(static_cast<uint32_t>(value));
+ }
+ case ColumnType::kId: {
+ if (value.type != SqlValue::Type::kLong)
+ return base::nullopt;
+ return row_map().IndexOf(static_cast<uint32_t>(value.long_value));
+ }
}
PERFETTO_FATAL("For GCC");
}
// Updates the given RowMap by only keeping rows where this column meets the
// given filter constraint.
- void FilterInto(FilterOp, int64_t value, RowMap*) const;
+ void FilterInto(FilterOp, SqlValue value, RowMap*) const;
// Returns a Constraint for each type of filter operation for this Column.
- Constraint eq(int64_t value) const {
+ Constraint eq(SqlValue value) const {
return Constraint{col_idx_, FilterOp::kEq, value};
}
- Constraint gt(int64_t value) const {
+ Constraint gt(SqlValue value) const {
return Constraint{col_idx_, FilterOp::kGt, value};
}
- Constraint lt(int64_t value) const {
+ Constraint lt(SqlValue value) const {
return Constraint{col_idx_, FilterOp::kLt, value};
}
@@ -144,6 +179,7 @@
enum ColumnType {
// Standard primitive types.
kInt64,
+ kString,
// Types generated on the fly.
kId,
@@ -153,18 +189,14 @@
ColumnType type,
Table* table,
uint32_t col_idx,
- uint32_t row_map_idx)
- : name_(name),
- table_(table),
- col_idx_(col_idx),
- row_map_idx_(row_map_idx),
- type_(type) {}
+ uint32_t row_map_idx);
Column(const Column&) = delete;
Column& operator=(const Column&) = delete;
const char* name_ = nullptr;
- Table* table_ = nullptr;
+ const Table* table_ = nullptr;
+ const StringPool* string_pool_ = nullptr;
uint32_t col_idx_ = 0;
uint32_t row_map_idx_ = 0;
@@ -172,6 +204,9 @@
union {
// Valid when |type_| == ColumnType::kInt64.
const SparseVector<int64_t>* int64_sv = nullptr;
+
+ // Valid when |type_| == ColumnType::kString.
+ const SparseVector<StringPool::Id>* string_sv;
} data_;
};
diff --git a/src/trace_processor/db/table.cc b/src/trace_processor/db/table.cc
index ac975de..ee7d41d 100644
--- a/src/trace_processor/db/table.cc
+++ b/src/trace_processor/db/table.cc
@@ -19,7 +19,7 @@
namespace perfetto {
namespace trace_processor {
-Table::Table(const Table* parent) {
+Table::Table(const StringPool* pool, const Table* parent) : string_pool_(pool) {
if (!parent)
return;
@@ -106,7 +106,7 @@
Table Table::LookupJoin(JoinKey left, const Table& other, JoinKey right) {
// The join table will have the same size and RowMaps as the left (this)
// table because the left column is indexing the right table.
- Table table(nullptr);
+ Table table(string_pool_, nullptr);
table.size_ = size_;
for (const RowMap& rm : row_maps_) {
table.row_maps_.emplace_back(rm.Copy());
@@ -129,9 +129,9 @@
// in the right table.
std::vector<uint32_t> indices(size_);
for (uint32_t i = 0; i < size_; ++i) {
- base::Optional<int64_t> val = left_col.Get(i);
- PERFETTO_CHECK(val.has_value());
- indices[i] = right_col.IndexOf(static_cast<uint32_t>(val.value())).value();
+ SqlValue val = left_col.Get(i);
+ PERFETTO_CHECK(val.type != SqlValue::Type::kNull);
+ indices[i] = right_col.IndexOf(val).value();
}
// Apply the computed RowMap to each of the right RowMaps, adding it to the
diff --git a/src/trace_processor/db/table.h b/src/trace_processor/db/table.h
index 127a44b..aa2addb 100644
--- a/src/trace_processor/db/table.h
+++ b/src/trace_processor/db/table.h
@@ -26,6 +26,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/optional.h"
#include "src/trace_processor/db/column.h"
+#include "src/trace_processor/string_pool.h"
namespace perfetto {
namespace trace_processor {
@@ -41,7 +42,7 @@
bool Next() { return ++row_ < table_->size(); }
// Returns the value at the current row for column |col_idx|.
- base::Optional<int64_t> Get(uint32_t col_idx) {
+ SqlValue Get(uint32_t col_idx) {
return table_->columns_[col_idx].Get(row_);
}
@@ -92,7 +93,7 @@
const std::vector<RowMap>& row_maps() const { return row_maps_; }
protected:
- explicit Table(const Table* parent);
+ Table(const StringPool* pool, const Table* parent);
std::vector<RowMap> row_maps_;
std::vector<Column> columns_;
@@ -105,6 +106,8 @@
// the Table pointer in each column to the Table being copied into.
Table(const Table& other) { *this = other; }
Table& operator=(const Table& other);
+
+ const StringPool* string_pool_ = nullptr;
};
} // namespace trace_processor
diff --git a/src/trace_processor/tables/macros_internal.h b/src/trace_processor/tables/macros_internal.h
index 08a6b34..1c0724c 100644
--- a/src/trace_processor/tables/macros_internal.h
+++ b/src/trace_processor/tables/macros_internal.h
@@ -38,7 +38,8 @@
// code size.
class MacroTable : public Table {
public:
- MacroTable(Table* parent) : Table(parent), parent_(parent) {
+ MacroTable(const StringPool* pool, Table* parent)
+ : Table(pool, parent), parent_(parent) {
if (!parent) {
columns_.emplace_back(
Column::IdColumn(this, static_cast<uint32_t>(columns_.size()),
@@ -131,8 +132,8 @@
#define PERFETTO_TP_TABLE_INTERNAL(class_name, parent_class_name, DEF) \
class class_name : public macros_internal::MacroTable { \
public: \
- class_name(parent_class_name* parent) \
- : macros_internal::MacroTable(parent), parent_(parent) { \
+ class_name(const StringPool* pool, parent_class_name* parent) \
+ : macros_internal::MacroTable(pool, parent), parent_(parent) { \
/* Expands to \
* columns_.emplace_back("col1", col1_, this, columns_.size(), \
* row_maps_.size() - 1); \
diff --git a/src/trace_processor/tables/macros_unittest.cc b/src/trace_processor/tables/macros_unittest.cc
index 76c5b53..3735241 100644
--- a/src/trace_processor/tables/macros_unittest.cc
+++ b/src/trace_processor/tables/macros_unittest.cc
@@ -40,50 +40,57 @@
NAME(TestCpuSliceTable) \
PARENT(PERFETTO_TP_TEST_SLICE_TABLE_DEF, C) \
C(int64_t, cpu) \
- C(int64_t, priority)
+ C(int64_t, priority) \
+ C(StringPool::Id, end_state)
PERFETTO_TP_TABLE(PERFETTO_TP_TEST_CPU_SLICE_TABLE_DEF);
TEST(TableMacrosUnittest, InsertParent) {
- TestEventTable event(nullptr);
- TestSliceTable slice(&event);
+ StringPool pool;
+ TestEventTable event(&pool, nullptr);
+ TestSliceTable slice(&pool, &event);
+
uint32_t id = event.Insert(100, 0);
ASSERT_EQ(id, 0u);
- ASSERT_EQ(event.ts().Get(0), 100);
- ASSERT_EQ(event.arg_set_id().Get(0), 0);
+ ASSERT_EQ(event.ts().Get(0), SqlValue::Long(100));
+ ASSERT_EQ(event.arg_set_id().Get(0), SqlValue::Long(0));
id = slice.Insert(200, 123, 10, 0);
ASSERT_EQ(id, 1u);
- ASSERT_EQ(event.ts().Get(1), 200);
- ASSERT_EQ(event.arg_set_id().Get(1), 123);
- ASSERT_EQ(slice.ts().Get(0), 200);
- ASSERT_EQ(slice.arg_set_id().Get(0), 123);
- ASSERT_EQ(slice.dur().Get(0), 10);
- ASSERT_EQ(slice.depth().Get(0), 0);
+ ASSERT_EQ(event.ts().Get(1), SqlValue::Long(200));
+ ASSERT_EQ(event.arg_set_id().Get(1), SqlValue::Long(123));
+ ASSERT_EQ(slice.ts().Get(0), SqlValue::Long(200));
+ ASSERT_EQ(slice.arg_set_id().Get(0), SqlValue::Long(123));
+ ASSERT_EQ(slice.dur().Get(0), SqlValue::Long(10));
+ ASSERT_EQ(slice.depth().Get(0), SqlValue::Long(0));
}
TEST(TableMacrosUnittest, InsertChild) {
- TestEventTable event(nullptr);
- TestSliceTable slice(&event);
- TestCpuSliceTable cpu_slice(&slice);
+ StringPool pool;
+ TestEventTable event(&pool, nullptr);
+ TestSliceTable slice(&pool, &event);
+ TestCpuSliceTable cpu_slice(&pool, &slice);
+
event.Insert(100, 0);
slice.Insert(200, 123, 10, 0);
- uint32_t id = cpu_slice.Insert(205, 456, 5, 1, 4, 1024);
+ auto reason = pool.InternString("R");
+ uint32_t id = cpu_slice.Insert(205, 456, 5, 1, 4, 1024, reason);
ASSERT_EQ(id, 2u);
- ASSERT_EQ(event.ts().Get(2), 205);
- ASSERT_EQ(event.arg_set_id().Get(2), 456);
+ ASSERT_EQ(event.ts().Get(2), SqlValue::Long(205));
+ ASSERT_EQ(event.arg_set_id().Get(2), SqlValue::Long(456));
- ASSERT_EQ(slice.ts().Get(1), 205);
- ASSERT_EQ(slice.arg_set_id().Get(1), 456);
- ASSERT_EQ(slice.dur().Get(1), 5);
- ASSERT_EQ(slice.depth().Get(1), 1);
+ ASSERT_EQ(slice.ts().Get(1), SqlValue::Long(205));
+ ASSERT_EQ(slice.arg_set_id().Get(1), SqlValue::Long(456));
+ ASSERT_EQ(slice.dur().Get(1), SqlValue::Long(5));
+ ASSERT_EQ(slice.depth().Get(1), SqlValue::Long(1));
- ASSERT_EQ(cpu_slice.ts().Get(0), 205);
- ASSERT_EQ(cpu_slice.arg_set_id().Get(0), 456);
- ASSERT_EQ(cpu_slice.dur().Get(0), 5);
- ASSERT_EQ(cpu_slice.depth().Get(0), 1);
- ASSERT_EQ(cpu_slice.cpu().Get(0), 4);
- ASSERT_EQ(cpu_slice.priority().Get(0), 1024);
+ ASSERT_EQ(cpu_slice.ts().Get(0), SqlValue::Long(205));
+ ASSERT_EQ(cpu_slice.arg_set_id().Get(0), SqlValue::Long(456));
+ ASSERT_EQ(cpu_slice.dur().Get(0), SqlValue::Long(5));
+ ASSERT_EQ(cpu_slice.depth().Get(0), SqlValue::Long(1));
+ ASSERT_EQ(cpu_slice.cpu().Get(0), SqlValue::Long(4));
+ ASSERT_EQ(cpu_slice.priority().Get(0), SqlValue::Long(1024));
+ ASSERT_EQ(cpu_slice.end_state().Get(0), SqlValue::String("R"));
}
} // namespace
diff --git a/test/cts/heapprofd_test_cts.cc b/test/cts/heapprofd_test_cts.cc
index d2f2383..15eafcb 100644
--- a/test/cts/heapprofd_test_cts.cc
+++ b/test/cts/heapprofd_test_cts.cc
@@ -144,7 +144,7 @@
// start tracing
helper.StartTracing(trace_config);
- helper.WaitForTracingDisabled(4000 /*ms*/);
+ helper.WaitForTracingDisabled(10000 /*ms*/);
helper.ReadData();
helper.WaitForReadData();
diff --git a/ui/src/frontend/thread_state_panel.ts b/ui/src/frontend/thread_state_panel.ts
index a65cea9..7a8fafd 100644
--- a/ui/src/frontend/thread_state_panel.ts
+++ b/ui/src/frontend/thread_state_panel.ts
@@ -39,9 +39,15 @@
m('th', `Start time`),
m('td',
`${
- timeToCode(attrs.ts - globals.state.traceTime.startSec)
- }`)),
- m('tr', m('th', `Duration`), m('td', `${timeToCode(attrs.dur)}`)),
+ timeToCode(
+ attrs.ts - globals.state.traceTime.startSec)}`)),
+ m('tr',
+ m('th', `Duration`),
+ m('td',
+ `${timeToCode(attrs.dur)} `,
+ m('a',
+ {href: 'http://b/140256335', target: '_blank'},
+ '(b/140256335)'))),
m('tr',
m('th', `State`),
m('td', `${translateState(attrs.state)}`)),