Merge "cleanup: update "next id" proto comment"
diff --git a/Android.bp b/Android.bp
index b65d3fe..73880ac 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8425,6 +8425,7 @@
"src/base/scoped_file_unittest.cc",
"src/base/small_vector_unittest.cc",
"src/base/status_or_unittest.cc",
+ "src/base/status_unittest.cc",
"src/base/string_splitter_unittest.cc",
"src/base/string_utils_unittest.cc",
"src/base/string_view_unittest.cc",
@@ -9409,6 +9410,18 @@
// GN: //src/trace_processor/db/overlays:overlays
filegroup {
name: "perfetto_src_trace_processor_db_overlays_overlays",
+ srcs: [
+ "src/trace_processor/db/overlays/null_overlay.cc",
+ "src/trace_processor/db/overlays/storage_overlay.cc",
+ ],
+}
+
+// GN: //src/trace_processor/db/overlays:unittests
+filegroup {
+ name: "perfetto_src_trace_processor_db_overlays_unittests",
+ srcs: [
+ "src/trace_processor/db/overlays/null_overlay_unittest.cc",
+ ],
}
// GN: //src/trace_processor/db/storage:storage
@@ -10188,6 +10201,7 @@
"src/trace_processor/prelude/functions/create_view_function.cc",
"src/trace_processor/prelude/functions/import.cc",
"src/trace_processor/prelude/functions/layout_functions.cc",
+ "src/trace_processor/prelude/functions/math.cc",
"src/trace_processor/prelude/functions/pprof_functions.cc",
"src/trace_processor/prelude/functions/sqlite3_str_split.cc",
"src/trace_processor/prelude/functions/stack_functions.cc",
@@ -10283,6 +10297,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -10379,6 +10394,7 @@
"src/trace_processor/sqlite/sql_stats_table.cc",
"src/trace_processor/sqlite/sqlite_engine.cc",
"src/trace_processor/sqlite/sqlite_table.cc",
+ "src/trace_processor/sqlite/sqlite_tokenizer.cc",
"src/trace_processor/sqlite/sqlite_utils.cc",
"src/trace_processor/sqlite/stats_table.cc",
],
@@ -10390,6 +10406,7 @@
srcs: [
"src/trace_processor/sqlite/db_sqlite_table_unittest.cc",
"src/trace_processor/sqlite/query_constraints_unittest.cc",
+ "src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc",
"src/trace_processor/sqlite/sqlite_utils_unittest.cc",
],
}
@@ -10412,6 +10429,8 @@
"src/trace_processor/stdlib/android/statsd.sql",
"src/trace_processor/stdlib/chrome/chrome_scrolls.sql",
"src/trace_processor/stdlib/chrome/cpu_powerups.sql",
+ "src/trace_processor/stdlib/chrome/histograms.sql",
+ "src/trace_processor/stdlib/chrome/speedometer.sql",
"src/trace_processor/stdlib/common/counters.sql",
"src/trace_processor/stdlib/common/cpus.sql",
"src/trace_processor/stdlib/common/metadata.sql",
@@ -10498,6 +10517,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -10513,6 +10533,7 @@
"src/trace_processor/tables/memory_tables_py.h",
"src/trace_processor/tables/metadata_tables_py.h",
"src/trace_processor/tables/profiler_tables_py.h",
+ "src/trace_processor/tables/sched_tables_py.h",
"src/trace_processor/tables/slice_tables_py.h",
"src/trace_processor/tables/trace_proto_tables_py.h",
"src/trace_processor/tables/track_tables_py.h",
@@ -10532,6 +10553,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -12053,6 +12075,7 @@
":perfetto_src_trace_processor_containers_unittests",
":perfetto_src_trace_processor_db_db",
":perfetto_src_trace_processor_db_overlays_overlays",
+ ":perfetto_src_trace_processor_db_overlays_unittests",
":perfetto_src_trace_processor_db_storage_storage",
":perfetto_src_trace_processor_db_storage_unittests",
":perfetto_src_trace_processor_db_unittests",
diff --git a/BUILD b/BUILD
index a62edb8..ebfc1cc 100644
--- a/BUILD
+++ b/BUILD
@@ -1277,6 +1277,9 @@
perfetto_filegroup(
name = "src_trace_processor_db_overlays_overlays",
srcs = [
+ "src/trace_processor/db/overlays/null_overlay.cc",
+ "src/trace_processor/db/overlays/null_overlay.h",
+ "src/trace_processor/db/overlays/storage_overlay.cc",
"src/trace_processor/db/overlays/storage_overlay.h",
"src/trace_processor/db/overlays/types.h",
],
@@ -2014,6 +2017,8 @@
"src/trace_processor/prelude/functions/import.h",
"src/trace_processor/prelude/functions/layout_functions.cc",
"src/trace_processor/prelude/functions/layout_functions.h",
+ "src/trace_processor/prelude/functions/math.cc",
+ "src/trace_processor/prelude/functions/math.h",
"src/trace_processor/prelude/functions/pprof_functions.cc",
"src/trace_processor/prelude/functions/pprof_functions.h",
"src/trace_processor/prelude/functions/sqlite3_str_split.cc",
@@ -2174,6 +2179,8 @@
"src/trace_processor/sqlite/sqlite_engine.h",
"src/trace_processor/sqlite/sqlite_table.cc",
"src/trace_processor/sqlite/sqlite_table.h",
+ "src/trace_processor/sqlite/sqlite_tokenizer.cc",
+ "src/trace_processor/sqlite/sqlite_tokenizer.h",
"src/trace_processor/sqlite/sqlite_utils.cc",
"src/trace_processor/sqlite/sqlite_utils.h",
"src/trace_processor/sqlite/stats_table.cc",
@@ -2213,6 +2220,8 @@
srcs = [
"src/trace_processor/stdlib/chrome/chrome_scrolls.sql",
"src/trace_processor/stdlib/chrome/cpu_powerups.sql",
+ "src/trace_processor/stdlib/chrome/histograms.sql",
+ "src/trace_processor/stdlib/chrome/speedometer.sql",
],
)
@@ -2293,6 +2302,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -2304,6 +2314,7 @@
"src/trace_processor/tables/memory_tables_py.h",
"src/trace_processor/tables/metadata_tables_py.h",
"src/trace_processor/tables/profiler_tables_py.h",
+ "src/trace_processor/tables/sched_tables_py.h",
"src/trace_processor/tables/slice_tables_py.h",
"src/trace_processor/tables/trace_proto_tables_py.h",
"src/trace_processor/tables/track_tables_py.h",
diff --git a/include/perfetto/base/status.h b/include/perfetto/base/status.h
index 2939357..f059184 100644
--- a/include/perfetto/base/status.h
+++ b/include/perfetto/base/status.h
@@ -17,7 +17,10 @@
#ifndef INCLUDE_PERFETTO_BASE_STATUS_H_
#define INCLUDE_PERFETTO_BASE_STATUS_H_
+#include <optional>
#include <string>
+#include <string_view>
+#include <vector>
#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
@@ -30,6 +33,10 @@
// This can used as the return type of functions which would usually return an
// bool for success or int for errno but also wants to add some string context
// (ususally for logging).
+//
+// Similar to absl::Status, an optional "payload" can also be included with more
+// context about the error. This allows passing additional metadata about the
+// error (e.g. location of errors, potential mitigations etc).
class PERFETTO_EXPORT_COMPONENT Status {
public:
Status() : ok_(true) {}
@@ -52,9 +59,49 @@
const std::string& message() const { return message_; }
const char* c_message() const { return message_.c_str(); }
+ //////////////////////////////////////////////////////////////////////////////
+ // Payload Management APIs
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Payloads can be attached to error statuses to provide additional context.
+ //
+ // Payloads are (key, value) pairs, where the key is a string acting as a
+ // unique "type URL" and the value is an opaque string. The "type URL" should
+ // be unique, follow the format of a URL and, ideally, documentation on how to
+ // interpret its associated data should be available.
+ //
+ // To attach a payload to a status object, call `Status::SetPayload()`.
+ // Similarly, to extract the payload from a status, call
+ // `Status::GetPayload()`.
+ //
+ // Note: the payload APIs are only meaningful to call when the status is an
+ // error. Otherwise, all methods are noops.
+
+ // Gets the payload for the given |type_url| if one exists.
+ //
+ // Will always return std::nullopt if |ok()|.
+ std::optional<std::string_view> GetPayload(std::string_view type_url);
+
+ // Sets the payload for the given key. The key should
+ //
+ // Will always do nothing if |ok()|.
+ void SetPayload(std::string_view type_url, std::string value);
+
+ // Erases the payload for the given string and returns true if the payload
+ // existed and was erased.
+ //
+ // Will always do nothing if |ok()|.
+ bool ErasePayload(std::string_view type_url);
+
private:
+ struct Payload {
+ std::string type_url;
+ std::string payload;
+ };
+
bool ok_ = false;
std::string message_;
+ std::vector<Payload> payloads_;
};
// Returns a status object which represents the Ok status.
diff --git a/include/perfetto/tracing/internal/data_source_type.h b/include/perfetto/tracing/internal/data_source_type.h
index aee830a..73c84be 100644
--- a/include/perfetto/tracing/internal/data_source_type.h
+++ b/include/perfetto/tracing/internal/data_source_type.h
@@ -167,8 +167,7 @@
InstancesIterator BeginIteration(
uint32_t cached_instances,
DataSourceThreadLocalState* tls_state,
- typename TracePointTraits::TracePointData trace_point_data)
- PERFETTO_ALWAYS_INLINE {
+ typename TracePointTraits::TracePointData trace_point_data) {
InstancesIterator it{};
it.cached_instances = cached_instances;
FirstActiveInstance<TracePointTraits>(&it, tls_state, trace_point_data);
@@ -185,8 +184,7 @@
template <typename TracePointTraits>
void NextIteration(InstancesIterator* iterator,
DataSourceThreadLocalState* tls_state,
- typename TracePointTraits::TracePointData trace_point_data)
- PERFETTO_ALWAYS_INLINE {
+ typename TracePointTraits::TracePointData trace_point_data) {
iterator->i++;
FirstActiveInstance<TracePointTraits>(iterator, tls_state,
trace_point_data);
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index d1e17a6..2ec348e 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -308,276 +308,25 @@
{category_index});
}
- // Once we've determined tracing to be enabled for this category, actually
- // write a trace event onto this thread's default track. Outlined to avoid
- // bloating code (mostly stack depth) at the actual trace point.
- //
- // The following combination of parameters is supported (in the given order):
- // - Zero or one track,
- // - Zero or one custom timestamp,
- // - Arbitrary number of debug annotations.
- // - Zero or one lambda.
-
- // Trace point which does not take a track or timestamp.
- template <typename CategoryType,
- typename EventNameType,
- typename... Arguments>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type,
- TrackEventInternal::kDefaultTrack,
- TrackEventInternal::GetTraceTime(),
- std::forward<Arguments>(args)...);
+ // The following methods forward all arguments to TraceForCategoryBody
+ // while casting string constants to const char*.
+ template <typename... Arguments>
+ static void TraceForCategory(Arguments&&... args) PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryBody(DecayStrType(args)...);
}
- // Trace point which takes a track, but not timestamp.
- // NOTE: Here track should be captured using universal reference (TrackType&&)
- // instead of const TrackType& to ensure that the proper overload is selected
- // (otherwise the compiler will fail to disambiguate between adding const& and
- // parsing track as a part of Arguments...).
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, std::forward<TrackType>(track),
- TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...);
+ template <typename... Arguments>
+ static void TraceForCategoryLegacy(Arguments&&... args)
+ PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyBody(DecayStrType(args)...);
}
- // Trace point which takes a timestamp, but not track.
- template <typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type,
- TrackEventInternal::kDefaultTrack,
- std::forward<TimestampType>(timestamp),
- std::forward<Arguments>(args)...);
+ template <typename... Arguments>
+ static void TraceForCategoryLegacyWithId(Arguments&&... args)
+ PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyWithIdBody(DecayStrType(args)...);
}
- // Trace point which takes a timestamp and a track.
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type,
- std::forward<TrackType>(track),
- std::forward<TimestampType>(timestamp),
- std::forward<Arguments>(args)...);
- }
-
- // Trace point with with a counter sample.
- template <typename CategoryType, typename EventNameType, typename ValueType>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType&,
- perfetto::protos::pbzero::TrackEvent::Type type,
- CounterTrack track,
- ValueType value) PERFETTO_ALWAYS_INLINE {
- PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
- TraceForCategory(instances, category, /*name=*/nullptr, type, track,
- TrackEventInternal::GetTraceTime(), value);
- }
-
- // Trace point with with a timestamp and a counter sample.
- template <typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type,
- typename ValueType>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType&,
- perfetto::protos::pbzero::TrackEvent::Type type,
- CounterTrack track,
- TimestampType timestamp,
- ValueType value) PERFETTO_ALWAYS_INLINE {
- PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
- TraceForCategoryImpl(
- instances, category, /*name=*/nullptr, type, track, timestamp,
- [&](EventContext event_ctx) {
- if (std::is_integral<ValueType>::value) {
- int64_t value_int64 = static_cast<int64_t>(value);
- if (track.is_incremental()) {
- TrackEventIncrementalState* incr_state =
- event_ctx.GetIncrementalState();
- PERFETTO_DCHECK(incr_state != nullptr);
- auto prv_value =
- incr_state->last_counter_value_per_track[track.uuid];
- event_ctx.event()->set_counter_value(value_int64 - prv_value);
- prv_value = value_int64;
- incr_state->last_counter_value_per_track[track.uuid] = prv_value;
- } else {
- event_ctx.event()->set_counter_value(value_int64);
- }
- } else {
- event_ctx.event()->set_double_counter_value(
- static_cast<double>(value));
- }
- });
- }
-
-// Additional trace points used in legacy macros.
-// It's possible to implement legacy macros using a common TraceForCategory,
-// by supplying a lambda that sets all necessary legacy fields. But this
-// results in a binary size bloat because every trace point generates its own
-// template instantiation with its own lambda. ICF can't eliminate those as
-// each lambda captures different variables and so the code is not completely
-// identical.
-// What we do instead is define additional TraceForCategoryLegacy templates
-// that take legacy arguments directly. Their instantiations can have the same
-// binary code for at least some macro invocations and so can be successfully
-// folded by the linker.
-#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type>
- static void TraceForCategoryLegacy(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type, track,
- TrackEventInternal::GetTraceTime(),
- [&](perfetto::EventContext ctx)
- PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- TrackEventLegacy::WriteLegacyEvent(
- std::move(ctx), phase, flags, args...);
- });
- }
-
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategoryLegacy(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, track, timestamp,
- [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- TrackEventLegacy::WriteLegacyEvent(std::move(ctx), phase, flags,
- args...);
- });
- }
-
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename ThreadIdType,
- typename LegacyIdType,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type>
- static void TraceForCategoryLegacyWithId(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- ThreadIdType thread_id,
- LegacyIdType legacy_id,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, track,
- TrackEventInternal::GetTraceTime(),
- [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
- TrackEventLegacy::WriteLegacyEventWithIdAndTid(
- std::move(ctx), phase, flags, trace_id, thread_id, args...);
- });
- }
-
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename ThreadIdType,
- typename LegacyIdType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategoryLegacyWithId(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- ThreadIdType thread_id,
- LegacyIdType legacy_id,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, track, timestamp,
- [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
- TrackEventLegacy::WriteLegacyEventWithIdAndTid(
- std::move(ctx), phase, flags, trace_id, thread_id, args...);
- });
- }
-#endif // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
-
// Initialize the track event library. Should be called before tracing is
// enabled.
static bool Register() {
@@ -638,6 +387,295 @@
const protos::gen::TrackEventConfig& GetConfig() const { return config_; }
private:
+ // The DecayStrType method is used to avoid unnecessary instantiations of
+ // templates on string constants of different sizes. Without it, strings
+ // of different lengths have different types: char[10], char[15] etc.
+ // DecayStrType forwards all types of arguments as is, with the exception
+ // of string constants which are all cast to const char*. This allows to
+ // avoid extra instantiations of TraceForCategory templates.
+ template <typename T>
+ static T&& DecayStrType(T&& t) {
+ return std::forward<T>(t);
+ }
+
+ static const char* DecayStrType(const char* t) { return t; }
+
+ // Once we've determined tracing to be enabled for this category, actually
+ // write a trace event onto this thread's default track. Outlined to avoid
+ // bloating code (mostly stack depth) at the actual trace point.
+ //
+ // The following combination of parameters is supported (in the given order):
+ // - Zero or one track,
+ // - Zero or one custom timestamp,
+ // - Arbitrary number of debug annotations.
+ // - Zero or one lambda.
+
+ // Trace point which does not take a track or timestamp.
+ template <typename CategoryType,
+ typename EventNameType,
+ typename... Arguments>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type,
+ TrackEventInternal::kDefaultTrack,
+ TrackEventInternal::GetTraceTime(),
+ std::forward<Arguments>(args)...);
+ }
+
+ // Trace point which takes a track, but not timestamp.
+ // NOTE: Here track should be captured using universal reference (TrackType&&)
+ // instead of const TrackType& to ensure that the proper overload is selected
+ // (otherwise the compiler will fail to disambiguate between adding const& and
+ // parsing track as a part of Arguments...).
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, std::forward<TrackType>(track),
+ TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...);
+ }
+
+ // Trace point which takes a timestamp, but not track.
+ template <typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type,
+ TrackEventInternal::kDefaultTrack,
+ std::forward<TimestampType>(timestamp),
+ std::forward<Arguments>(args)...);
+ }
+
+ // Trace point which takes a timestamp and a track.
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type,
+ std::forward<TrackType>(track),
+ std::forward<TimestampType>(timestamp),
+ std::forward<Arguments>(args)...);
+ }
+
+ // Trace point with with a counter sample.
+ template <typename CategoryType, typename EventNameType, typename ValueType>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType&,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ CounterTrack track,
+ ValueType value) PERFETTO_ALWAYS_INLINE {
+ PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
+ TraceForCategory(instances, category, /*name=*/nullptr, type, track,
+ TrackEventInternal::GetTraceTime(), value);
+ }
+
+ // Trace point with with a timestamp and a counter sample.
+ template <typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type,
+ typename ValueType>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType&,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ CounterTrack track,
+ TimestampType timestamp,
+ ValueType value) PERFETTO_ALWAYS_INLINE {
+ PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
+ TraceForCategoryImpl(
+ instances, category, /*name=*/nullptr, type, track, timestamp,
+ [&](EventContext event_ctx) {
+ if (std::is_integral<ValueType>::value) {
+ int64_t value_int64 = static_cast<int64_t>(value);
+ if (track.is_incremental()) {
+ TrackEventIncrementalState* incr_state =
+ event_ctx.GetIncrementalState();
+ PERFETTO_DCHECK(incr_state != nullptr);
+ auto prv_value =
+ incr_state->last_counter_value_per_track[track.uuid];
+ event_ctx.event()->set_counter_value(value_int64 - prv_value);
+ prv_value = value_int64;
+ incr_state->last_counter_value_per_track[track.uuid] = prv_value;
+ } else {
+ event_ctx.event()->set_counter_value(value_int64);
+ }
+ } else {
+ event_ctx.event()->set_double_counter_value(
+ static_cast<double>(value));
+ }
+ });
+ }
+
+// Additional trace points used in legacy macros.
+// It's possible to implement legacy macros using a common TraceForCategory,
+// by supplying a lambda that sets all necessary legacy fields. But this
+// results in a binary size bloat because every trace point generates its own
+// template instantiation with its own lambda. ICF can't eliminate those as
+// each lambda captures different variables and so the code is not completely
+// identical.
+// What we do instead is define additional TraceForCategoryLegacy templates
+// that take legacy arguments directly. Their instantiations can have the same
+// binary code for at least some macro invocations and so can be successfully
+// folded by the linker.
+#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryLegacyBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type, track,
+ TrackEventInternal::GetTraceTime(),
+ [&](perfetto::EventContext ctx)
+ PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ TrackEventLegacy::WriteLegacyEvent(
+ std::move(ctx), phase, flags, args...);
+ });
+ }
+
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryLegacyBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, track, timestamp,
+ [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ TrackEventLegacy::WriteLegacyEvent(std::move(ctx), phase, flags,
+ args...);
+ });
+ }
+
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename ThreadIdType,
+ typename LegacyIdType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryLegacyWithIdBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ ThreadIdType thread_id,
+ LegacyIdType legacy_id,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, track,
+ TrackEventInternal::GetTraceTime(),
+ [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
+ TrackEventLegacy::WriteLegacyEventWithIdAndTid(
+ std::move(ctx), phase, flags, trace_id, thread_id, args...);
+ });
+ }
+
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename ThreadIdType,
+ typename LegacyIdType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryLegacyWithIdBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ ThreadIdType thread_id,
+ LegacyIdType legacy_id,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, track, timestamp,
+ [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
+ TrackEventLegacy::WriteLegacyEventWithIdAndTid(
+ std::move(ctx), phase, flags, trace_id, thread_id, args...);
+ });
+ }
+#endif // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+
// Each category has its own enabled/disabled state, stored in the category
// registry.
struct CategoryTracePointTraits {
diff --git a/python/setup.py b/python/setup.py
index db49cac..a484eda 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -8,7 +8,7 @@
],
package_data={'perfetto.trace_processor': ['*.descriptor']},
include_package_data=True,
- version='0.6.0',
+ version='0.7.0',
license='apache-2.0',
description='Python API for Perfetto\'s Trace Processor',
author='Perfetto',
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 8d48bf0..7dfce6b 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -199,6 +199,7 @@
"scoped_file_unittest.cc",
"small_vector_unittest.cc",
"status_or_unittest.cc",
+ "status_unittest.cc",
"string_splitter_unittest.cc",
"string_utils_unittest.cc",
"string_view_unittest.cc",
diff --git a/src/base/status.cc b/src/base/status.cc
index 30ccc47..d3c13e8 100644
--- a/src/base/status.cc
+++ b/src/base/status.cc
@@ -17,6 +17,7 @@
#include "perfetto/base/status.h"
#include <stdarg.h>
+#include <algorithm>
namespace perfetto {
namespace base {
@@ -31,5 +32,42 @@
return status;
}
+std::optional<std::string_view> Status::GetPayload(std::string_view type_url) {
+ if (ok()) {
+ return std::nullopt;
+ }
+ for (const auto& kv : payloads_) {
+ if (kv.type_url == type_url) {
+ return kv.payload;
+ }
+ }
+ return std::nullopt;
+}
+
+void Status::SetPayload(std::string_view type_url, std::string value) {
+ if (ok()) {
+ return;
+ }
+ for (auto& kv : payloads_) {
+ if (kv.type_url == type_url) {
+ kv.payload = value;
+ return;
+ }
+ }
+ payloads_.push_back(Payload{std::string(type_url), std::move(value)});
+}
+
+bool Status::ErasePayload(std::string_view type_url) {
+ if (ok()) {
+ return false;
+ }
+ auto it = std::remove_if(
+ payloads_.begin(), payloads_.end(),
+ [type_url](const Payload& p) { return p.type_url == type_url; });
+ bool erased = it != payloads_.end();
+ payloads_.erase(it, payloads_.end());
+ return erased;
+}
+
} // namespace base
} // namespace perfetto
diff --git a/src/base/status_unittest.cc b/src/base/status_unittest.cc
new file mode 100644
index 0000000..df42b31
--- /dev/null
+++ b/src/base/status_unittest.cc
@@ -0,0 +1,67 @@
+/*
+ * 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 at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/base/status.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace base {
+
+TEST(StatusTest, GetMissingPayload) {
+ base::Status status = base::ErrStatus("Error");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), std::nullopt);
+}
+
+TEST(StatusTest, SetThenGetPayload) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), "payload_value");
+}
+
+TEST(StatusTest, SetEraseGetPayload) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ ASSERT_TRUE(status.ErasePayload("test.foo.com/bar"));
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), std::nullopt);
+}
+
+TEST(StatusTest, SetOverride) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ status.SetPayload("test.foo.com/bar", "other_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), "other_value");
+}
+
+TEST(StatusTest, SetGetOk) {
+ base::Status status = base::OkStatus();
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), std::nullopt);
+}
+
+TEST(StatusTest, SetMultipleAndDuplicate) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ status.SetPayload("test.foo.com/bar1", "1");
+ status.SetPayload("test.foo.com/bar2", "2");
+ status.SetPayload("test.foo.com/bar", "other_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), "other_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar1"), "1");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar2"), "2");
+}
+
+} // namespace base
+} // namespace perfetto
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index bcfbf28..55c3c9d 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -874,8 +874,9 @@
for (int j = 0; j < nested_enum->value_count(); ++j) {
const EnumValueDescriptor* value = nested_enum->value(j);
- stub_h_->Print("static const $class$ $name$ = $class$::$name$;\n",
- "class", nested_enum->name(), "name", value->name());
+ stub_h_->Print(
+ "static inline const $class$ $name$ = $class$::$name$;\n", "class",
+ nested_enum->name(), "name", value->name());
}
}
diff --git a/src/tools/proto_merger/proto_file_serializer.cc b/src/tools/proto_merger/proto_file_serializer.cc
index dca5a07..c94a101 100644
--- a/src/tools/proto_merger/proto_file_serializer.cc
+++ b/src/tools/proto_merger/proto_file_serializer.cc
@@ -70,8 +70,11 @@
std::string output;
output += " [";
- for (const auto& option : options) {
- output += option.key + " = " + option.value;
+ size_t n = options.size();
+ for (size_t i = 0; i < n; i++) {
+ output += options[i].key + " = " + options[i].value;
+ if (i != n - 1)
+ output += ", ";
}
output += "]";
return output;
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6ff25a9..04814a6 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -268,6 +268,7 @@
":top_level_unittests",
"containers:unittests",
"db:unittests",
+ "db/overlays:unittests",
"db/storage:unittests",
"importers/android_bugreport:unittests",
"importers/common:unittests",
diff --git a/src/trace_processor/db/overlays/BUILD.gn b/src/trace_processor/db/overlays/BUILD.gn
index c1d50d8..f0b3e72 100644
--- a/src/trace_processor/db/overlays/BUILD.gn
+++ b/src/trace_processor/db/overlays/BUILD.gn
@@ -12,13 +12,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import("../../../../gn/perfetto_tp_tables.gni")
+import("../../../../gn/test.gni")
+
source_set("overlays") {
sources = [
+ "null_overlay.cc",
+ "null_overlay.h",
+ "storage_overlay.cc",
"storage_overlay.h",
"types.h",
]
deps = [
"../../../../gn:default_deps",
+ "../../../base",
"../../containers",
]
}
+
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ sources = [ "null_overlay_unittest.cc" ]
+ deps = [
+ ":overlays",
+ "../../../../gn:default_deps",
+ "../../../../gn:gtest_and_gmock",
+ ]
+}
diff --git a/src/trace_processor/db/overlays/null_overlay.cc b/src/trace_processor/db/overlays/null_overlay.cc
new file mode 100644
index 0000000..c5f5f66
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay.cc
@@ -0,0 +1,131 @@
+/*
+ * 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 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/db/overlays/null_overlay.h"
+#include "perfetto/ext/base/flat_hash_map.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+using Range = RowMap::Range;
+
+StorageRange NullOverlay::MapToStorageRange(TableRange t_range) const {
+ uint32_t start = non_null_->CountSetBits(t_range.range.start);
+ uint32_t end = non_null_->CountSetBits(t_range.range.end);
+
+ return StorageRange({Range(start, end)});
+}
+
+TableBitVector NullOverlay::MapToTableBitVector(StorageBitVector s_bv) const {
+ BitVector res = non_null_->Copy();
+ res.UpdateSetBits(s_bv.bv);
+ return {std::move(res)};
+}
+
+BitVector NullOverlay::IsStorageLookupRequired(
+ OverlayOp op,
+ const TableIndexVector& t_iv) const {
+ PERFETTO_DCHECK(t_iv.indices.size() <= non_null_->size());
+
+ if (op != OverlayOp::kOther)
+ return BitVector();
+
+ BitVector in_storage(static_cast<uint32_t>(t_iv.indices.size()), false);
+
+ // For each index in TableIndexVector check whether this index is in storage.
+ for (uint32_t i = 0; i < t_iv.indices.size(); ++i) {
+ if (non_null_->IsSet(t_iv.indices[i]))
+ in_storage.Set(i);
+ }
+
+ return in_storage;
+}
+
+StorageIndexVector NullOverlay::MapToStorageIndexVector(
+ TableIndexVector t_iv_with_idx_in_storage) const {
+ PERFETTO_DCHECK(t_iv_with_idx_in_storage.indices.size() <=
+ non_null_->CountSetBits());
+
+ std::vector<uint32_t> storage_index_vector;
+ storage_index_vector.reserve(t_iv_with_idx_in_storage.indices.size());
+ for (auto t_idx : t_iv_with_idx_in_storage.indices) {
+ storage_index_vector.push_back(non_null_->CountSetBits(t_idx));
+ }
+
+ return StorageIndexVector({std::move(storage_index_vector)});
+}
+
+BitVector NullOverlay::IndexSearch(
+ OverlayOp op,
+ const TableIndexVector& t_iv_overlay_idx) const {
+ if (op == OverlayOp::kOther)
+ return BitVector();
+
+ BitVector res(static_cast<uint32_t>(t_iv_overlay_idx.indices.size()), false);
+ if (op == OverlayOp::kIsNull) {
+ for (uint32_t i = 0; i < res.size(); ++i) {
+ if (!non_null_->IsSet(t_iv_overlay_idx.indices[i]))
+ res.Set(i);
+ }
+ return res;
+ }
+
+ PERFETTO_DCHECK(op == OverlayOp::kIsNotNull);
+ for (uint32_t i = 0; i < res.size(); ++i) {
+ if (non_null_->IsSet(t_iv_overlay_idx.indices[i]))
+ res.Set(i);
+ }
+ return res;
+}
+
+CostEstimatePerRow NullOverlay::EstimateCostPerRow(OverlayOp op) const {
+ // TODO(b/283763282): Replace with benchmarked data.
+ CostEstimatePerRow res;
+
+ // Two |BitVector::CountSetBits| calls.
+ res.to_storage_range = 100;
+
+ // Cost of |BitVector::UpdateSetBits|
+ res.to_table_bit_vector = 100;
+
+ if (op == OverlayOp::kOther) {
+ // Cost of |BitVector::IsSet| and |BitVector::Set|
+ res.is_storage_search_required = 10;
+
+ // Cost of iterating all set bits and looping the index vector divided by
+ // number of indices.
+ res.map_to_storage_index_vector = 100;
+
+ // Won't be called.
+ res.index_search = 0;
+ } else {
+ // Cost of creating trivial BitVector.
+ res.is_storage_search_required = 0;
+
+ // Won't be called
+ res.map_to_storage_index_vector = 0;
+
+ // Cost of calling |BitVector::IsSet|
+ res.index_search = 10;
+ }
+
+ return res;
+}
+
+} // namespace overlays
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/null_overlay.h b/src/trace_processor/db/overlays/null_overlay.h
new file mode 100644
index 0000000..93da2c0
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay.h
@@ -0,0 +1,54 @@
+/*
+ * 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 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_DB_OVERLAYS_NULL_OVERLAY_H_
+#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
+
+#include "src/trace_processor/db/overlays/storage_overlay.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+// Introduces the layer of nullability - spreads out the storage with nulls
+// using BitVector.
+class NullOverlay : public StorageOverlay {
+ public:
+ explicit NullOverlay(BitVector* null) : non_null_(std::move(null)) {}
+
+ StorageRange MapToStorageRange(TableRange) const override;
+
+ TableBitVector MapToTableBitVector(StorageBitVector) const override;
+
+ BitVector IsStorageLookupRequired(OverlayOp,
+ const TableIndexVector&) const override;
+
+ StorageIndexVector MapToStorageIndexVector(TableIndexVector) const override;
+
+ BitVector IndexSearch(OverlayOp, const TableIndexVector&) const override;
+
+ CostEstimatePerRow EstimateCostPerRow(OverlayOp) const override;
+
+ private:
+ // Non null data in the overlay.
+ BitVector* non_null_;
+};
+
+} // namespace overlays
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
diff --git a/src/trace_processor/db/overlays/null_overlay_unittest.cc b/src/trace_processor/db/overlays/null_overlay_unittest.cc
new file mode 100644
index 0000000..6f80eba
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay_unittest.cc
@@ -0,0 +1,129 @@
+/*
+ * 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 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/db/overlays/null_overlay.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+namespace {
+
+TEST(NullOverlay, MapToStorageRangeOutsideBoundary) {
+ BitVector bv{0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+ NullOverlay overlay(&bv);
+ StorageRange r = overlay.MapToStorageRange({RowMap::Range(1, 6)});
+
+ ASSERT_EQ(r.range.start, 0u);
+ ASSERT_EQ(r.range.end, 2u);
+}
+
+TEST(NullOverlay, MapToStorageRangeOnBoundary) {
+ BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+ NullOverlay overlay(&bv);
+ StorageRange r = overlay.MapToStorageRange({RowMap::Range(3, 8)});
+
+ ASSERT_EQ(r.range.start, 1u);
+ ASSERT_EQ(r.range.end, 4u);
+}
+
+TEST(NullOverlay, MapToTableBitVector) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullOverlay overlay(&bv);
+
+ BitVector storage_bv{0, 1, 0, 1};
+ TableBitVector table_bv =
+ overlay.MapToTableBitVector({std::move(storage_bv)});
+
+ ASSERT_EQ(table_bv.bv.CountSetBits(), 2u);
+ ASSERT_TRUE(table_bv.bv.IsSet(2));
+ ASSERT_TRUE(table_bv.bv.IsSet(6));
+}
+
+TEST(NullOverlay, IsStorageLookupRequiredNullOp) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullOverlay overlay(&bv);
+
+ std::vector<uint32_t> table_idx{0, 2, 4, 6};
+ BitVector lookup_bv =
+ overlay.IsStorageLookupRequired(OverlayOp::kIsNull, {table_idx});
+
+ ASSERT_EQ(lookup_bv.size(), 0u);
+}
+
+TEST(NullOverlay, IsStorageLookupRequiredOtherOp) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullOverlay overlay(&bv);
+
+ std::vector<uint32_t> table_idx{0, 2, 4, 6};
+ BitVector lookup_bv =
+ overlay.IsStorageLookupRequired(OverlayOp::kOther, {table_idx});
+
+ ASSERT_EQ(lookup_bv.size(), 4u);
+ ASSERT_EQ(lookup_bv.CountSetBits(), 2u);
+ ASSERT_TRUE(lookup_bv.IsSet(1));
+ ASSERT_TRUE(lookup_bv.IsSet(3));
+}
+
+TEST(NullOverlay, MapToStorageIndexVector) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullOverlay overlay(&bv);
+
+ std::vector<uint32_t> table_idx{1, 5, 2};
+ StorageIndexVector storage_iv = overlay.MapToStorageIndexVector({table_idx});
+
+ std::vector<uint32_t> res{0, 2, 1};
+ ASSERT_EQ(storage_iv.indices, res);
+}
+
+TEST(NullOverlay, IndexSearchOtherOp) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullOverlay overlay(&bv);
+
+ std::vector<uint32_t> table_idx{0, 3, 4};
+ BitVector idx_search_bv = overlay.IndexSearch(OverlayOp::kOther, {table_idx});
+
+ ASSERT_EQ(idx_search_bv.size(), 0u);
+}
+
+TEST(NullOverlay, IndexSearchIsNullOp) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullOverlay overlay(&bv);
+
+ std::vector<uint32_t> table_idx{0, 3, 4};
+ BitVector idx_search_bv =
+ overlay.IndexSearch(OverlayOp::kIsNull, {table_idx});
+
+ ASSERT_EQ(idx_search_bv.size(), 3u);
+ ASSERT_EQ(idx_search_bv.CountSetBits(), 3u);
+}
+
+TEST(NullOverlay, IndexSearchIsNotNullOp) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullOverlay overlay(&bv);
+
+ std::vector<uint32_t> table_idx{0, 3, 4};
+ BitVector idx_search_bv =
+ overlay.IndexSearch(OverlayOp::kIsNotNull, {table_idx});
+
+ ASSERT_EQ(idx_search_bv.size(), 3u);
+ ASSERT_EQ(idx_search_bv.CountSetBits(), 0u);
+}
+
+} // namespace
+} // namespace overlays
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/storage_overlay.cc b/src/trace_processor/db/overlays/storage_overlay.cc
new file mode 100644
index 0000000..56897f2
--- /dev/null
+++ b/src/trace_processor/db/overlays/storage_overlay.cc
@@ -0,0 +1,27 @@
+/*
+ * 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 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/db/overlays/storage_overlay.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+StorageOverlay::~StorageOverlay() = default;
+
+} // namespace overlays
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/common/parser_types.h b/src/trace_processor/importers/common/parser_types.h
index 2b5648a..7290626 100644
--- a/src/trace_processor/importers/common/parser_types.h
+++ b/src/trace_processor/importers/common/parser_types.h
@@ -35,10 +35,12 @@
struct alignas(8) InlineSchedWaking {
int32_t pid;
- int32_t target_cpu;
- int32_t prio;
+ uint16_t target_cpu;
+ uint16_t prio;
StringPool::Id comm;
+ uint16_t common_flags;
};
+static_assert(sizeof(InlineSchedWaking) == 16);
struct alignas(8) JsonEvent {
std::string value;
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index ec5f264..6ccf70d 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -1069,9 +1069,9 @@
}
using protos::pbzero::FtraceEvent;
SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(context_);
- sched_tracker->PushSchedWakingCompact(cpu, ts,
- static_cast<uint32_t>(data.pid),
- data.target_cpu, data.prio, data.comm);
+ sched_tracker->PushSchedWakingCompact(
+ cpu, ts, static_cast<uint32_t>(data.pid), data.target_cpu, data.prio,
+ data.comm, data.common_flags);
return util::OkStatus();
}
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 67fccfe..c699ab9 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -247,6 +247,7 @@
auto tcpu_it = compact.waking_target_cpu(&parse_error);
auto prio_it = compact.waking_prio(&parse_error);
auto comm_it = compact.waking_comm_index(&parse_error);
+ auto common_flags_it = compact.waking_common_flags(&parse_error);
for (; timestamp_it && pid_it && tcpu_it && prio_it && comm_it;
++timestamp_it, ++pid_it, ++tcpu_it, ++prio_it, ++comm_it) {
@@ -261,8 +262,13 @@
event.comm = string_table[*comm_it];
event.pid = *pid_it;
- event.target_cpu = *tcpu_it;
- event.prio = *prio_it;
+ event.target_cpu = static_cast<uint16_t>(*tcpu_it);
+ event.prio = static_cast<uint16_t>(*prio_it);
+
+ if (common_flags_it) {
+ event.common_flags = static_cast<uint16_t>(*common_flags_it);
+ common_flags_it++;
+ }
base::StatusOr<int64_t> timestamp =
ResolveTraceTime(context_, clock_id, event_timestamp);
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index 68fe3b7..09459c4 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -212,9 +212,10 @@
void SchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
int64_t ts,
uint32_t wakee_pid,
- int32_t target_cpu,
- int32_t prio,
- StringId comm_id) {
+ uint16_t target_cpu,
+ uint16_t prio,
+ StringId comm_id,
+ uint16_t common_flags) {
// At this stage all events should be globally timestamp ordered.
if (ts < context_->event_tracker->max_timestamp()) {
PERFETTO_ELOG(
@@ -239,10 +240,15 @@
auto curr_utid = pending_sched->last_utid;
if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
+ tables::FtraceEventTable::Row row;
+ row.ts = ts;
+ row.name = sched_waking_id_;
+ row.cpu = cpu;
+ row.utid = curr_utid;
+ row.common_flags = common_flags;
+
// Add an entry to the raw table.
- RawId id = context_->storage->mutable_ftrace_event_table()
- ->Insert({ts, sched_waking_id_, cpu, curr_utid})
- .id;
+ RawId id = context_->storage->mutable_ftrace_event_table()->Insert(row).id;
using SW = protos::pbzero::SchedWakingFtraceEvent;
auto inserter = context_->args_tracker->AddArgsTo(id);
@@ -258,8 +264,8 @@
// Add a waking entry to the ThreadState table.
auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
- ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(ts, wakee_utid,
- curr_utid);
+ ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(
+ ts, wakee_utid, curr_utid, common_flags);
}
PERFETTO_ALWAYS_INLINE
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.h b/src/trace_processor/importers/ftrace/sched_event_tracker.h
index beebcfe..745b7c3 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.h
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.h
@@ -73,9 +73,10 @@
void PushSchedWakingCompact(uint32_t cpu,
int64_t ts,
uint32_t wakee_pid,
- int32_t target_cpu,
- int32_t prio,
- StringId comm_id);
+ uint16_t target_cpu,
+ uint16_t prio,
+ StringId comm_id,
+ uint16_t common_flags);
private:
// Information retained from the preceding sched_switch seen on a given cpu.
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker.cc b/src/trace_processor/importers/ftrace/thread_state_tracker.cc
index 3c6b123..a80d6a4 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.cc
+++ b/src/trace_processor/importers/ftrace/thread_state_tracker.cc
@@ -50,7 +50,8 @@
void ThreadStateTracker::PushWakingEvent(int64_t event_ts,
UniqueTid utid,
- UniqueTid waker_utid) {
+ UniqueTid waker_utid,
+ std::optional<uint16_t> common_flags) {
// Only open new runnable state if thread already had a sched switch event.
if (!HasPreviousRowNumbersForUtid(utid)) {
return;
@@ -62,12 +63,20 @@
// is running), we just ignore the waking event. See b/186509316 for details
// and an example on when this happens. Only blocked events can be waken up.
if (!IsBlocked(last_row_ref.state())) {
+ // If we receive a waking event while we are not blocked, we ignore this
+ // in the |thread_state| table but we track in the |sched_wakeup| table.
+ // The |thread_state_id| in |sched_wakeup| is the current running/runnable
+ // event.
+ storage_->mutable_spurious_sched_wakeup_table()->Insert(
+ {event_ts, prev_row_numbers_for_thread_[utid]->last_row.row_number(),
+ CommonFlagsToIrqContext(*common_flags), utid, waker_utid});
return;
}
// Close the sleeping state and open runnable state.
ClosePendingState(event_ts, utid, false);
- AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid);
+ AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid,
+ common_flags);
}
void ThreadStateTracker::PushNewTaskEvent(int64_t event_ts,
@@ -102,8 +111,9 @@
void ThreadStateTracker::AddOpenState(int64_t ts,
UniqueTid utid,
StringId state,
- std::optional<uint32_t> cpu,
- std::optional<UniqueTid> waker_utid) {
+ std::optional<uint16_t> cpu,
+ std::optional<UniqueTid> waker_utid,
+ std::optional<uint16_t> common_flags) {
// Ignore utid 0 because it corresponds to the swapper thread which doesn't
// make sense to insert.
if (utid == 0)
@@ -117,6 +127,10 @@
row.dur = -1;
row.utid = utid;
row.state = state;
+ if (common_flags.has_value()) {
+ row.irq_context = CommonFlagsToIrqContext(*common_flags);
+ }
+
auto row_num = storage_->mutable_thread_state_table()->Insert(row).row_number;
if (utid >= prev_row_numbers_for_thread_.size()) {
@@ -136,6 +150,14 @@
}
}
+uint32_t ThreadStateTracker::CommonFlagsToIrqContext(uint32_t common_flags) {
+ // If common_flags contains TRACE_FLAG_HARDIRQ | TRACE_FLAG_SOFTIRQ, wakeup
+ // was emitted in interrupt context.
+ // See:
+ // https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/include/trace/trace_events.h
+ return common_flags & (0x08 | 0x10) ? 1 : 0;
+}
+
void ThreadStateTracker::ClosePendingState(int64_t end_ts,
UniqueTid utid,
bool data_loss) {
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker.h b/src/trace_processor/importers/ftrace/thread_state_tracker.h
index 7faae9c..839ad31 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.h
+++ b/src/trace_processor/importers/ftrace/thread_state_tracker.h
@@ -49,7 +49,10 @@
UniqueTid next_utid);
// Will add a runnable state for utid and close the previously blocked one.
- void PushWakingEvent(int64_t event_ts, UniqueTid utid, UniqueTid waker_utid);
+ void PushWakingEvent(int64_t event_ts,
+ UniqueTid utid,
+ UniqueTid waker_utid,
+ std::optional<uint16_t> common_flags = std::nullopt);
// Will add a runnable state for utid. For a new task there are no previous
// states to close.
@@ -64,10 +67,13 @@
void AddOpenState(int64_t ts,
UniqueTid utid,
StringId state,
- std::optional<uint32_t> cpu = std::nullopt,
- std::optional<UniqueTid> waker_utid = std::nullopt);
+ std::optional<uint16_t> cpu = std::nullopt,
+ std::optional<UniqueTid> waker_utid = std::nullopt,
+ std::optional<uint16_t> common_flags = std::nullopt);
void ClosePendingState(int64_t end_ts, UniqueTid utid, bool data_loss);
+ uint32_t CommonFlagsToIrqContext(uint32_t common_flags);
+
bool IsRunning(StringId state);
bool IsBlocked(StringId state);
bool IsRunnable(StringId state);
diff --git a/src/trace_processor/importers/proto/android_camera_event_module.h b/src/trace_processor/importers/proto/android_camera_event_module.h
index 1d55081..e9df6c4 100644
--- a/src/trace_processor/importers/proto/android_camera_event_module.h
+++ b/src/trace_processor/importers/proto/android_camera_event_module.h
@@ -24,6 +24,7 @@
#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "src/trace_processor/importers/common/parser_types.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/importers/proto/statsd_module.h b/src/trace_processor/importers/proto/statsd_module.h
index 430b7a2..653c1eb 100644
--- a/src/trace_processor/importers/proto/statsd_module.h
+++ b/src/trace_processor/importers/proto/statsd_module.h
@@ -26,6 +26,7 @@
#include "src/trace_processor/importers/common/trace_parser.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
index 3538f7d..0724ba7 100644
--- a/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
+++ b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
@@ -126,6 +126,8 @@
OR s.name GLOB '*CancellableContinuationImpl*'
OR s.name GLOB 'relayoutWindow*'
OR s.name GLOB 'ImageDecoder#decode*'
+ OR s.name GLOB 'NotificationStackScrollLayout#onMeasure'
+ OR s.name GLOB 'ExpNotRow#*'
)
UNION ALL
SELECT
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql
index 9461d2c..47f1876 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql
@@ -18,6 +18,37 @@
SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
+DROP VIEW IF EXISTS chrome_scroll_jank_v2;
+
+CREATE VIEW chrome_scroll_jank_v2
+AS
+SELECT
+ 100.0 * scroll_jank_processing_ms / scroll_processing_ms
+ AS scroll_jank_percentage,
+ *
+FROM
+ (
+ SELECT
+ COALESCE(SUM(jank.dur), 0) / 1.0e6 AS scroll_processing_ms,
+ COALESCE(
+ SUM(
+ CASE
+ WHEN
+ jank.jank
+ AND cause.cause_of_jank != 'RendererCompositorQueueingDelay'
+ THEN jank.dur
+ ELSE 0
+ END),
+ 0)
+ / 1.0e6 AS scroll_jank_processing_ms
+ FROM
+ scroll_event_latency_jank AS jank
+ LEFT JOIN
+ event_latency_scroll_jank_cause AS cause
+ ON
+ jank.id = cause.slice_id
+ );
+
DROP VIEW IF EXISTS chrome_scroll_jank_v2_output;
CREATE VIEW chrome_scroll_jank_v2_output
@@ -31,31 +62,4 @@
'scroll_jank_percentage',
scroll_jank_percentage)
FROM
- (
- SELECT
- 100.0 * scroll_jank_processing_ms / scroll_processing_ms
- AS scroll_jank_percentage,
- *
- FROM
- (
- SELECT
- COALESCE(SUM(jank.dur), 0) / 1.0e6 AS scroll_processing_ms,
- COALESCE(
- SUM(
- CASE
- WHEN
- jank.jank
- AND cause.cause_of_jank != 'RendererCompositorQueueingDelay'
- THEN jank.dur
- ELSE 0
- END),
- 0)
- / 1.0e6 AS scroll_jank_processing_ms
- FROM
- scroll_event_latency_jank AS jank
- LEFT JOIN
- event_latency_scroll_jank_cause AS cause
- ON
- jank.id = cause.slice_id
- )
- );
+ chrome_scroll_jank_v2;
diff --git a/src/trace_processor/prelude/functions/BUILD.gn b/src/trace_processor/prelude/functions/BUILD.gn
index f2f22ba..ddbf904 100644
--- a/src/trace_processor/prelude/functions/BUILD.gn
+++ b/src/trace_processor/prelude/functions/BUILD.gn
@@ -29,6 +29,8 @@
"import.h",
"layout_functions.cc",
"layout_functions.h",
+ "math.cc",
+ "math.h",
"pprof_functions.cc",
"pprof_functions.h",
"sqlite3_str_split.cc",
diff --git a/src/trace_processor/prelude/functions/math.cc b/src/trace_processor/prelude/functions/math.cc
new file mode 100644
index 0000000..5310501
--- /dev/null
+++ b/src/trace_processor/prelude/functions/math.cc
@@ -0,0 +1,86 @@
+// 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 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/prelude/functions/math.h"
+
+#include <cmath>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto::trace_processor {
+
+namespace {
+
+struct Ln : public SqlFunction {
+ static base::Status Run(Context*,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors&) {
+ PERFETTO_CHECK(argc == 1);
+ switch (sqlite3_value_numeric_type(argv[0])) {
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ double value = sqlite3_value_double(argv[0]);
+ if (value > 0.0) {
+ out = SqlValue::Double(std::log(value));
+ }
+ break;
+ }
+ case SqlValue::kNull:
+ case SqlValue::kString:
+ case SqlValue::kBytes:
+ break;
+ }
+
+ return base::OkStatus();
+ }
+};
+
+struct Exp : public SqlFunction {
+ static base::Status Run(Context*,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors&) {
+ PERFETTO_CHECK(argc == 1);
+ switch (sqlite3_value_numeric_type(argv[0])) {
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ out = SqlValue::Double(std::exp(sqlite3_value_double(argv[0])));
+ break;
+ case SqlValue::kNull:
+ case SqlValue::kString:
+ case SqlValue::kBytes:
+ break;
+ }
+
+ return base::OkStatus();
+ }
+};
+
+} // namespace
+
+base::Status RegisterMathFunctions(SqliteEngine& engine) {
+ RETURN_IF_ERROR(engine.RegisterSqlFunction<Ln>("ln", 1, nullptr, true));
+ return engine.RegisterSqlFunction<Exp>("exp", 1, nullptr, true);
+}
+
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/prelude/functions/math.h b/src/trace_processor/prelude/functions/math.h
new file mode 100644
index 0000000..42e23f5
--- /dev/null
+++ b/src/trace_processor/prelude/functions/math.h
@@ -0,0 +1,31 @@
+// 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 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_PRELUDE_FUNCTIONS_MATH_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_MATH_H_
+
+#include "perfetto/base/status.h"
+
+namespace perfetto::trace_processor {
+
+class SqliteEngine;
+
+// Registers LN and EXP.
+// We do not compile the SQLite library with -DSQLITE_ENABLE_MATH_FUNCTIONS so
+// these functions are not provided by default.
+base::Status RegisterMathFunctions(SqliteEngine& engine);
+
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_MATH_H_
diff --git a/src/trace_processor/prelude/table_functions/tables.py b/src/trace_processor/prelude/table_functions/tables.py
index 4ffc526..32e4550 100644
--- a/src/trace_processor/prelude/table_functions/tables.py
+++ b/src/trace_processor/prelude/table_functions/tables.py
@@ -28,7 +28,7 @@
from src.trace_processor.tables.metadata_tables import PROCESS_TABLE
from src.trace_processor.tables.profiler_tables import STACK_PROFILE_CALLSITE_TABLE
from src.trace_processor.tables.slice_tables import SLICE_TABLE
-from src.trace_processor.tables.slice_tables import SCHED_SLICE_TABLE
+from src.trace_processor.tables.sched_tables import SCHED_SLICE_TABLE
ANCESTOR_SLICE_TABLE = Table(
python_module=__file__,
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index 7045815..b35605a 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -28,6 +28,8 @@
"sqlite_engine.h",
"sqlite_table.cc",
"sqlite_table.h",
+ "sqlite_tokenizer.cc",
+ "sqlite_tokenizer.h",
"sqlite_utils.cc",
"sqlite_utils.h",
"sqlite_utils.h",
@@ -72,6 +74,7 @@
sources = [
"db_sqlite_table_unittest.cc",
"query_constraints_unittest.cc",
+ "sqlite_tokenizer_unittest.cc",
"sqlite_utils_unittest.cc",
]
deps = [
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer.cc b/src/trace_processor/sqlite/sqlite_tokenizer.cc
new file mode 100644
index 0000000..1766baa
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_tokenizer.cc
@@ -0,0 +1,448 @@
+/*
+ * 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 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/sqlite/sqlite_tokenizer.h"
+
+#include <ctype.h>
+#include <sqlite3.h>
+#include <optional>
+#include <string_view>
+
+#include "perfetto/base/compiler.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// The contents of this file are ~copied from SQLite with some modifications to
+// minimize the amount copied: i.e. if we can call a libc function/public SQLite
+// API instead of a private one.
+//
+// The changes are as follows:
+// 1. Remove all ifdefs to only keep branches we actually use
+// 2. Change handling of |CC_KYWD0| to remove distinction between different
+// SQLite kewords, reducing how many things we need to copy over.
+// 3. Constants are changed from be macro defines to be values in
+// |SqliteTokenType|.
+
+namespace {
+
+const unsigned char sqlite3CtypeMap[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
+ 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
+ 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
+
+ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
+ 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
+ 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
+ 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */
+};
+
+#define CC_X 0 /* The letter 'x', or start of BLOB literal */
+#define CC_KYWD0 1 /* First letter of a keyword */
+#define CC_KYWD 2 /* Alphabetics or '_'. Usable in a keyword */
+#define CC_DIGIT 3 /* Digits */
+#define CC_DOLLAR 4 /* '$' */
+#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */
+#define CC_VARNUM 6 /* '?'. Numeric SQL variables */
+#define CC_SPACE 7 /* Space characters */
+#define CC_QUOTE 8 /* '"', '\'', or '`'. String literals, quoted ids */
+#define CC_QUOTE2 9 /* '['. [...] style quoted ids */
+#define CC_PIPE 10 /* '|'. Bitwise OR or concatenate */
+#define CC_MINUS 11 /* '-'. Minus or SQL-style comment */
+#define CC_LT 12 /* '<'. Part of < or <= or <> */
+#define CC_GT 13 /* '>'. Part of > or >= */
+#define CC_EQ 14 /* '='. Part of = or == */
+#define CC_BANG 15 /* '!'. Part of != */
+#define CC_SLASH 16 /* '/'. / or c-style comment */
+#define CC_LP 17 /* '(' */
+#define CC_RP 18 /* ')' */
+#define CC_SEMI 19 /* ';' */
+#define CC_PLUS 20 /* '+' */
+#define CC_STAR 21 /* '*' */
+#define CC_PERCENT 22 /* '%' */
+#define CC_COMMA 23 /* ',' */
+#define CC_AND 24 /* '&' */
+#define CC_TILDA 25 /* '~' */
+#define CC_DOT 26 /* '.' */
+#define CC_ID 27 /* unicode characters usable in IDs */
+#define CC_NUL 29 /* 0x00 */
+#define CC_BOM 30 /* First byte of UTF8 BOM: 0xEF 0xBB 0xBF */
+
+// clang-format off
+static const unsigned char aiClass[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 29, 28, 28, 28, 28, 28, 28, 28, 28, 7, 7, 28, 7, 7, 28, 28,
+/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
+/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
+/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 9, 28, 28, 28, 2,
+/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 28, 10, 28, 25, 28,
+/* 8x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 9x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Ax */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Cx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Dx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Ex */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 30,
+/* Fx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27
+};
+// clang-format on
+
+#define IdChar(C) ((sqlite3CtypeMap[static_cast<unsigned char>(C)] & 0x46) != 0)
+
+// Copy of |sqlite3GetToken| for use by the PerfettoSql transpiler.
+//
+// We copy this function because |sqlite3GetToken| is static to sqlite3.c
+// in most distributions of SQLite so we cannot call it from our code.
+//
+// While we could redefine SQLITE_PRIVATE, pragmatically that will not fly in
+// all the places we build trace processor so we need to resort to making a
+// copy.
+int GetSqliteToken(const unsigned char* z, SqliteTokenType* tokenType) {
+ int i, c;
+ switch (aiClass[*z]) { /* Switch on the character-class of the first byte
+ ** of the token. See the comment on the CC_ defines
+ ** above. */
+ case CC_SPACE: {
+ for (i = 1; isspace(z[i]); i++) {
+ }
+ *tokenType = SqliteTokenType::TK_SPACE;
+ return i;
+ }
+ case CC_MINUS: {
+ if (z[1] == '-') {
+ for (i = 2; (c = z[i]) != 0 && c != '\n'; i++) {
+ }
+ *tokenType = SqliteTokenType::TK_SPACE; /* IMP: R-22934-25134 */
+ return i;
+ } else if (z[1] == '>') {
+ *tokenType = SqliteTokenType::TK_PTR;
+ return 2 + (z[2] == '>');
+ }
+ *tokenType = SqliteTokenType::TK_MINUS;
+ return 1;
+ }
+ case CC_LP: {
+ *tokenType = SqliteTokenType::TK_LP;
+ return 1;
+ }
+ case CC_RP: {
+ *tokenType = SqliteTokenType::TK_RP;
+ return 1;
+ }
+ case CC_SEMI: {
+ *tokenType = SqliteTokenType::TK_SEMI;
+ return 1;
+ }
+ case CC_PLUS: {
+ *tokenType = SqliteTokenType::TK_PLUS;
+ return 1;
+ }
+ case CC_STAR: {
+ *tokenType = SqliteTokenType::TK_STAR;
+ return 1;
+ }
+ case CC_SLASH: {
+ if (z[1] != '*' || z[2] == 0) {
+ *tokenType = SqliteTokenType::TK_SLASH;
+ return 1;
+ }
+ for (i = 3, c = z[2]; (c != '*' || z[i] != '/') && (c = z[i]) != 0; i++) {
+ }
+ if (c)
+ i++;
+ *tokenType = SqliteTokenType::TK_SPACE; /* IMP: R-22934-25134 */
+ return i;
+ }
+ case CC_PERCENT: {
+ *tokenType = SqliteTokenType::TK_REM;
+ return 1;
+ }
+ case CC_EQ: {
+ *tokenType = SqliteTokenType::TK_EQ;
+ return 1 + (z[1] == '=');
+ }
+ case CC_LT: {
+ if ((c = z[1]) == '=') {
+ *tokenType = SqliteTokenType::TK_LE;
+ return 2;
+ } else if (c == '>') {
+ *tokenType = SqliteTokenType::TK_NE;
+ return 2;
+ } else if (c == '<') {
+ *tokenType = SqliteTokenType::TK_LSHIFT;
+ return 2;
+ } else {
+ *tokenType = SqliteTokenType::TK_LT;
+ return 1;
+ }
+ }
+ case CC_GT: {
+ if ((c = z[1]) == '=') {
+ *tokenType = SqliteTokenType::TK_GE;
+ return 2;
+ } else if (c == '>') {
+ *tokenType = SqliteTokenType::TK_RSHIFT;
+ return 2;
+ } else {
+ *tokenType = SqliteTokenType::TK_GT;
+ return 1;
+ }
+ }
+ case CC_BANG: {
+ if (z[1] != '=') {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return 1;
+ } else {
+ *tokenType = SqliteTokenType::TK_NE;
+ return 2;
+ }
+ }
+ case CC_PIPE: {
+ if (z[1] != '|') {
+ *tokenType = SqliteTokenType::TK_BITOR;
+ return 1;
+ } else {
+ *tokenType = SqliteTokenType::TK_CONCAT;
+ return 2;
+ }
+ }
+ case CC_COMMA: {
+ *tokenType = SqliteTokenType::TK_COMMA;
+ return 1;
+ }
+ case CC_AND: {
+ *tokenType = SqliteTokenType::TK_BITAND;
+ return 1;
+ }
+ case CC_TILDA: {
+ *tokenType = SqliteTokenType::TK_BITNOT;
+ return 1;
+ }
+ case CC_QUOTE: {
+ int delim = z[0];
+ for (i = 1; (c = z[i]) != 0; i++) {
+ if (c == delim) {
+ if (z[i + 1] == delim) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ }
+ if (c == '\'') {
+ *tokenType = SqliteTokenType::TK_STRING;
+ return i + 1;
+ } else if (c != 0) {
+ *tokenType = SqliteTokenType::TK_ID;
+ return i + 1;
+ } else {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return i;
+ }
+ }
+ case CC_DOT: {
+ if (!isdigit(z[1])) {
+ *tokenType = SqliteTokenType::TK_DOT;
+ return 1;
+ }
+ [[fallthrough]];
+ }
+ case CC_DIGIT: {
+ *tokenType = SqliteTokenType::TK_INTEGER;
+ if (z[0] == '0' && (z[1] == 'x' || z[1] == 'X') && isxdigit(z[2])) {
+ for (i = 3; isxdigit(z[i]); i++) {
+ }
+ return i;
+ }
+ for (i = 0; isxdigit(z[i]); i++) {
+ }
+ if (z[i] == '.') {
+ i++;
+ while (isxdigit(z[i])) {
+ i++;
+ }
+ *tokenType = SqliteTokenType::TK_FLOAT;
+ }
+ if ((z[i] == 'e' || z[i] == 'E') &&
+ (isdigit(z[i + 1]) ||
+ ((z[i + 1] == '+' || z[i + 1] == '-') && isdigit(z[i + 2])))) {
+ i += 2;
+ while (isdigit(z[i])) {
+ i++;
+ }
+ *tokenType = SqliteTokenType::TK_FLOAT;
+ }
+ while (IdChar(z[i])) {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ i++;
+ }
+ return i;
+ }
+ case CC_QUOTE2: {
+ for (i = 1, c = z[0]; c != ']' && (c = z[i]) != 0; i++) {
+ }
+ *tokenType =
+ c == ']' ? SqliteTokenType::TK_ID : SqliteTokenType::TK_ILLEGAL;
+ return i;
+ }
+ case CC_VARNUM: {
+ *tokenType = SqliteTokenType::TK_VARIABLE;
+ for (i = 1; isdigit(z[i]); i++) {
+ }
+ return i;
+ }
+ case CC_DOLLAR:
+ case CC_VARALPHA: {
+ int n = 0;
+ *tokenType = SqliteTokenType::TK_VARIABLE;
+ for (i = 1; (c = z[i]) != 0; i++) {
+ if (IdChar(c)) {
+ n++;
+ } else if (c == '(' && n > 0) {
+ do {
+ i++;
+ } while ((c = z[i]) != 0 && !isspace(c) && c != ')');
+ if (c == ')') {
+ i++;
+ } else {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ }
+ break;
+ } else if (c == ':' && z[i + 1] == ':') {
+ i++;
+ } else {
+ break;
+ }
+ }
+ if (n == 0)
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return i;
+ }
+ case CC_KYWD0: {
+ for (i = 1; aiClass[z[i]] <= CC_KYWD; i++) {
+ }
+ if (IdChar(z[i])) {
+ /* This token started out using characters that can appear in keywords,
+ ** but z[i] is a character not allowed within keywords, so this must
+ ** be an identifier instead */
+ i++;
+ break;
+ }
+ if (sqlite3_keyword_check(reinterpret_cast<const char*>(z), i)) {
+ *tokenType = SqliteTokenType::TK_GENERIC_KEYWORD;
+ } else {
+ *tokenType = SqliteTokenType::TK_ID;
+ }
+ return i;
+ }
+ case CC_X: {
+ if (z[1] == '\'') {
+ *tokenType = SqliteTokenType::TK_BLOB;
+ for (i = 2; isdigit(z[i]); i++) {
+ }
+ if (z[i] != '\'' || i % 2) {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ while (z[i] && z[i] != '\'') {
+ i++;
+ }
+ }
+ if (z[i])
+ i++;
+ return i;
+ }
+ [[fallthrough]];
+ }
+ case CC_KYWD:
+ case CC_ID: {
+ i = 1;
+ break;
+ }
+ case CC_BOM: {
+ if (z[1] == 0xbb && z[2] == 0xbf) {
+ *tokenType = SqliteTokenType::TK_SPACE;
+ return 3;
+ }
+ i = 1;
+ break;
+ }
+ case CC_NUL: {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return 0;
+ }
+ default: {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return 1;
+ }
+ }
+ while (IdChar(z[i])) {
+ i++;
+ }
+ *tokenType = SqliteTokenType::TK_ID;
+ return i;
+}
+
+} // namespace
+
+SqliteTokenizer::SqliteTokenizer(const char* sql) : ptr_(sql) {}
+
+SqliteTokenizer::Token SqliteTokenizer::Next() {
+ Token token;
+ const char* start = ptr_;
+ int n = GetSqliteToken(unsigned_ptr(), &token.token_type);
+ ptr_ += n;
+ token.str = std::string_view(start, static_cast<uint32_t>(n));
+ return token;
+}
+
+SqliteTokenizer::Token SqliteTokenizer::NextNonWhitespace() {
+ Token t;
+ for (t = Next(); t.token_type == SqliteTokenType::TK_SPACE; t = Next()) {
+ }
+ return t;
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer.h b/src/trace_processor/sqlite/sqlite_tokenizer.h
new file mode 100644
index 0000000..0d3bfb6
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_tokenizer.h
@@ -0,0 +1,112 @@
+/*
+ * 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 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_SQLITE_SQLITE_TOKENIZER_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TOKENIZER_H_
+
+#include <optional>
+#include <string_view>
+
+namespace perfetto {
+namespace trace_processor {
+
+// List of token types returnable by |SqliteTokenizer|
+// 1:1 matches the defintions in SQLite.
+enum class SqliteTokenType : uint32_t {
+ TK_SEMI = 1,
+ TK_LP = 22,
+ TK_RP = 23,
+ TK_COMMA = 25,
+ TK_NE = 52,
+ TK_EQ = 53,
+ TK_GT = 54,
+ TK_LE = 55,
+ TK_LT = 56,
+ TK_GE = 57,
+ TK_ID = 59,
+ TK_BITAND = 102,
+ TK_BITOR = 103,
+ TK_LSHIFT = 104,
+ TK_RSHIFT = 105,
+ TK_PLUS = 106,
+ TK_MINUS = 107,
+ TK_STAR = 108,
+ TK_SLASH = 109,
+ TK_REM = 110,
+ TK_CONCAT = 111,
+ TK_PTR = 112,
+ TK_BITNOT = 114,
+ TK_STRING = 117,
+ TK_DOT = 141,
+ TK_FLOAT = 153,
+ TK_BLOB = 154,
+ TK_INTEGER = 155,
+ TK_VARIABLE = 156,
+ TK_SPACE = 183,
+ TK_ILLEGAL = 184,
+
+ // Generic constant which replaces all the keywords in SQLite as we do not
+ // care about the distinguishing between the vast majority of them.
+ TK_GENERIC_KEYWORD = 1000,
+};
+
+// Tokenizes SQL statements according to SQLite SQL language specification:
+// https://www2.sqlite.org/hlr40000.html
+//
+// Usage of this class:
+// SqliteTokenizer tzr;
+// tzr.Reset(my_sql_string.c_str());
+// for (auto t = tzr.Next(); t.token_type != TK_SEMI; t = tzr.Next()) {
+// // Handle t here
+// }
+class SqliteTokenizer {
+ public:
+ // A single SQL token according to the SQLite standard.
+ struct Token {
+ // The string contents of the token.
+ std::string_view str;
+
+ // The type of the token.
+ SqliteTokenType token_type;
+
+ bool operator==(const Token& o) const {
+ return str == o.str && token_type == o.token_type;
+ }
+ };
+
+ explicit SqliteTokenizer(const char* sql);
+
+ // Returns the next SQL token.
+ Token Next();
+
+ // Returns the next SQL token which is not of type TK_SPACE.
+ Token NextNonWhitespace();
+
+ // Returns the pointer to the start of the next token which will be returned.
+ const char* ptr() const { return ptr_; }
+
+ private:
+ const unsigned char* unsigned_ptr() const {
+ return reinterpret_cast<const unsigned char*>(ptr_);
+ }
+
+ const char* ptr_ = nullptr;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TOKENIZER_H_
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc b/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc
new file mode 100644
index 0000000..44946b7
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc
@@ -0,0 +1,67 @@
+/*
+ * 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 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/sqlite/sqlite_tokenizer.h"
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using Token = SqliteTokenizer::Token;
+using Type = SqliteTokenType;
+
+class SqliteTokenizerTest : public ::testing::Test {
+ protected:
+ std::vector<SqliteTokenizer::Token> Tokenize(const char* ptr) {
+ SqliteTokenizer tokenizer(ptr);
+ std::vector<SqliteTokenizer::Token> tokens;
+ for (auto t = tokenizer.Next(); !t.str.empty(); t = tokenizer.Next()) {
+ tokens.push_back(t);
+ }
+ return tokens;
+ }
+};
+
+TEST_F(SqliteTokenizerTest, EmptyString) {
+ ASSERT_THAT(Tokenize(""), testing::IsEmpty());
+}
+
+TEST_F(SqliteTokenizerTest, OnlySpace) {
+ ASSERT_THAT(Tokenize(" "), testing::ElementsAre(Token{" ", Type::TK_SPACE}));
+}
+
+TEST_F(SqliteTokenizerTest, SpaceColon) {
+ ASSERT_THAT(Tokenize(" ;"), testing::ElementsAre(Token{" ", Type::TK_SPACE},
+ Token{";", Type::TK_SEMI}));
+}
+
+TEST_F(SqliteTokenizerTest, Select) {
+ ASSERT_THAT(
+ Tokenize("SELECT * FROM slice;"),
+ testing::ElementsAre(
+ Token{"SELECT", Type::TK_GENERIC_KEYWORD}, Token{" ", Type::TK_SPACE},
+ Token{"*", Type::TK_STAR}, Token{" ", Type::TK_SPACE},
+ Token{"FROM", Type::TK_GENERIC_KEYWORD}, Token{" ", Type::TK_SPACE},
+ Token{"slice", Type::TK_ID}, Token{";", Type::TK_SEMI}));
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/stdlib/chrome/BUILD.gn b/src/trace_processor/stdlib/chrome/BUILD.gn
index 7ba85fd..e9881c6 100644
--- a/src/trace_processor/stdlib/chrome/BUILD.gn
+++ b/src/trace_processor/stdlib/chrome/BUILD.gn
@@ -18,5 +18,7 @@
sources = [
"chrome_scrolls.sql",
"cpu_powerups.sql",
+ "histograms.sql",
+ "speedometer.sql",
]
}
diff --git a/src/trace_processor/stdlib/chrome/histograms.sql b/src/trace_processor/stdlib/chrome/histograms.sql
new file mode 100644
index 0000000..db354e3
--- /dev/null
+++ b/src/trace_processor/stdlib/chrome/histograms.sql
@@ -0,0 +1,46 @@
+-- Copyright 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 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.
+
+DROP VIEW IF EXISTS chrome_histograms;
+
+-- A helper view on top of the histogram events emitted by Chrome.
+-- Requires "disabled-by-default-histogram_samples" Chrome category.
+--
+-- @column name The name of the histogram.
+-- @column value The value of the histogram sample.
+-- @column ts Alias of |slice.ts|.
+-- @column thread_name Thread name.
+-- @column utid Utid of the thread.
+-- @column tid Tid of the thread.
+-- @column process_name Process name.
+-- @column upid Upid of the process.
+-- @column pid Pid of the process.
+CREATE VIEW chrome_histograms AS
+SELECT
+ extract_arg(slice.arg_set_id, "chrome_histogram_sample.name") as name,
+ extract_arg(slice.arg_set_id, "chrome_histogram_sample.sample") as value,
+ ts,
+ thread.name as thread_name,
+ thread.utid as utid,
+ thread.tid as tid,
+ process.name as process_name,
+ process.upid as upid,
+ process.pid as pid
+FROM slice
+JOIN thread_track ON thread_track.id = slice.track_id
+JOIN thread USING (utid)
+JOIN process USING (upid)
+WHERE
+ slice.name = "HistogramSample"
+ AND category = "disabled-by-default-histogram_samples";
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/chrome/speedometer.sql b/src/trace_processor/stdlib/chrome/speedometer.sql
new file mode 100644
index 0000000..652940a
--- /dev/null
+++ b/src/trace_processor/stdlib/chrome/speedometer.sql
@@ -0,0 +1,202 @@
+-- Copyright 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 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.
+
+-- Annotates a trace with Speedometer 2.1 related information.
+--
+-- The scripts below analyse traces with the following tracing options
+-- enabled:
+--
+-- - Chromium:
+-- "blink.user_timing".
+--
+-- NOTE: A regular speedometer run (e.g. from the website) will generate the
+-- required events. No need to add any extra JS or anything.
+--
+-- Noteworthy tables:
+-- speedometer_mark: List of marks (event slices) emitted by Speedometer.
+-- These are the points in time Speedometer makes a clock reading to
+-- compute intervals of time for the final score.
+-- speedometer_measure_slice: Augmented slices for Speedometer measurements.
+-- These are the intervals of time Speedometer uses to compute the final
+-- score.
+-- speedometer_iteration_slice: Slice that covers one Speedometer iteration
+-- and has the total_time and score for it. If you average all the scores
+-- over all iterations you get the final Speedometer score for the run.
+
+-- List of marks (event slices) emitted by Speedometer.
+-- These are the points in time Speedometer makes a clock reading to compute
+-- intervals of time for the final score.
+--
+-- @column slice_id Slice this data refers to.
+-- @column iteration Speedometer iteration the mark belongs to.
+-- @column suite_name Suite name
+-- @column test_name Test name
+-- @column mark_type Type of mark (start, sync-end, async-end)
+CREATE VIEW internal_chrome_speedometer_mark
+AS
+WITH
+ speedometer_21_suite_name(suite_name) AS (
+ VALUES
+ ('VanillaJS-TodoMVC'),
+ ('Vanilla-ES2015-TodoMVC'),
+ ('Vanilla-ES2015-Babel-Webpack-TodoMVC'),
+ ('React-TodoMVC'),
+ ('React-Redux-TodoMVC'),
+ ('EmberJS-TodoMVC'),
+ ('EmberJS-Debug-TodoMVC'),
+ ('BackboneJS-TodoMVC'),
+ ('AngularJS-TodoMVC'),
+ ('Angular2-TypeScript-TodoMVC'),
+ ('VueJS-TodoMVC'),
+ ('jQuery-TodoMVC'),
+ ('Preact-TodoMVC'),
+ ('Inferno-TodoMVC'),
+ ('Elm-TodoMVC'),
+ ('Flight-TodoMVC')
+ ),
+ speedometer_21_test_name(test_name) AS (
+ VALUES
+ ('Adding100Items'),
+ ('CompletingAllItems'),
+ -- This seems to be an issue with Speedometer 2.1. All tests delete all items,
+ -- but for some reason the test names do not match for all suites.
+ ('DeletingAllItems'),
+ ('DeletingItems')
+ ),
+ speedometer_21_test_mark_type(mark_type) AS (
+ VALUES
+ ('start'),
+ ('sync-end'),
+ ('async-end')
+ ),
+ -- Make sure we only look at slices with names we expect.
+ speedometer_mark_name AS (
+ SELECT
+ s.suite_name || '.' || t.test_name || '-' || m.mark_type AS name,
+ s.suite_name,
+ t.test_name,
+ m.mark_type
+ FROM
+ speedometer_21_suite_name AS s,
+ speedometer_21_test_name AS t,
+ speedometer_21_test_mark_type AS m
+ )
+SELECT
+ s.id AS slice_id,
+ RANK() OVER (PARTITION BY name ORDER BY ts ASC) AS iteration,
+ m.suite_name,
+ m.test_name,
+ m.mark_type
+FROM slice AS s
+JOIN speedometer_mark_name AS m
+ USING (name)
+WHERE category = 'blink.user_timing';
+
+-- Augmented slices for Speedometer measurements.
+-- These are the intervals of time Speedometer uses to compute the final score.
+-- There are two intervals that are measured for every test: sync and async
+-- sync is the time between the start and sync-end marks, async is the time
+-- between the sync-end and async-end marks.
+--
+-- @column iteration Speedometer iteration the mark belongs to.
+-- @column suite_name Suite name
+-- @column test_name Test name
+-- @column measure_type Type of the measure (sync or async)
+-- @column ts Start timestamp of the measure
+-- @column dur Duration of the measure
+CREATE VIEW chrome_speedometer_measure
+AS
+WITH
+ -- Get the 3 test timestamps (start, sync-end, async-end) in one row. Using a
+ -- the LAG window function and partitioning by test. 2 out of the 3 rows
+ -- generated per test will have some NULL ts values.
+ augmented AS (
+ SELECT
+ iteration,
+ suite_name,
+ test_name,
+ ts AS async_end_ts,
+ LAG(ts, 1)
+ OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC)
+ AS sync_end_ts,
+ LAG(ts, 2)
+ OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC)
+ AS start_ts,
+ COUNT()
+ OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC)
+ AS mark_count
+ FROM internal_chrome_speedometer_mark
+ JOIN slice
+ USING (slice_id)
+ ),
+ filtered AS (
+ SELECT *
+ FROM augmented
+ -- This server 2 purposes: make sure we have all the marks (think truncated
+ -- trace), and remove the NULL ts values due to the LAG window function.
+ WHERE mark_count = 3
+ )
+SELECT
+ iteration,
+ suite_name,
+ test_name,
+ 'async' AS measure_type,
+ sync_end_ts AS ts,
+ async_end_ts - sync_end_ts AS dur
+FROM filtered
+UNION ALL
+SELECT
+ iteration,
+ suite_name,
+ test_name,
+ 'sync' AS measure_type,
+ start_ts AS ts,
+ sync_end_ts - start_ts AS dur
+FROM filtered;
+
+-- Slice that covers one Speedometer iteration.
+-- This slice is actually estimated as a default Speedometer run will not emit
+-- marks to cover this interval. The metrics associated are the same ones
+-- Speedometer would output, but note we use ns precision (Speedometer uses
+-- ~100us) so the actual values might differ a bit. Also note Speedometer
+-- returns the values in ms these here and in ns.
+--
+-- @column iteration Speedometer iteration.
+-- @column ts Start timestamp of the iteration
+-- @column dur Duration of the iteration
+-- @column total Total duration of the measures in this iteration
+-- @column mean Average suite duration for this iteration.
+-- @column geomean Geometric mean of the suite durations for this iteration.
+-- @column score Speedometer score for this iteration (The total score for a
+-- run in the average of all iteration scores).
+CREATE VIEW chrome_speedometer_iteration
+AS
+SELECT
+ iteration,
+ MIN(start) AS ts,
+ MAX(end) - MIN(start) AS dur,
+ SUM(suite_total) AS total,
+ AVG(suite_total)AS mean,
+ -- Compute geometric mean using LN instead of multiplication to prevent
+ -- overflows
+ EXP(AVG(LN(suite_total))) AS geomean,
+ 1e9 / EXP(AVG(LN(suite_total))) * 60 / 3 AS score
+FROM
+ (
+ SELECT
+ iteration, SUM(dur) AS suite_total, MIN(ts) AS start, MAX(ts + dur) AS end
+ FROM chrome_speedometer_measure
+ GROUP BY suite_name, iteration
+ )
+GROUP BY iteration;
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 66fc940..67bdd0b 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -42,6 +42,7 @@
#include "src/trace_processor/tables/memory_tables_py.h"
#include "src/trace_processor/tables/metadata_tables_py.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/trace_proto_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
@@ -457,6 +458,13 @@
const tables::SliceTable& slice_table() const { return slice_table_; }
tables::SliceTable* mutable_slice_table() { return &slice_table_; }
+ const tables::SpuriousSchedWakeupTable& spurious_sched_wakeup_table() const {
+ return spurious_sched_wakeup_table_;
+ }
+ tables::SpuriousSchedWakeupTable* mutable_spurious_sched_wakeup_table() {
+ return &spurious_sched_wakeup_table_;
+ }
+
const tables::FlowTable& flow_table() const { return flow_table_; }
tables::FlowTable* mutable_flow_table() { return &flow_table_; }
@@ -888,6 +896,8 @@
// Slices from CPU scheduling data.
tables::SchedSliceTable sched_slice_table_{&string_pool_};
+ tables::SpuriousSchedWakeupTable spurious_sched_wakeup_table_{&string_pool_};
+
// Additional attributes for virtual track slices (sub-type of
// NestableSlices).
VirtualTrackSlices virtual_track_slices_;
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index 9ec397f..b8d69a8 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -23,6 +23,7 @@
"memory_tables.py",
"metadata_tables.py",
"profiler_tables.py",
+ "sched_tables.py",
"slice_tables.py",
"trace_proto_tables.py",
"track_tables.py",
diff --git a/src/trace_processor/tables/metadata_tables.py b/src/trace_processor/tables/metadata_tables.py
index 5d800d7..093db01 100644
--- a/src/trace_processor/tables/metadata_tables.py
+++ b/src/trace_processor/tables/metadata_tables.py
@@ -170,6 +170,7 @@
C('cpu', CppUint32()),
C('utid', CppTableId(THREAD_TABLE)),
C('arg_set_id', CppUint32()),
+ C('common_flags', CppUint32())
],
tabledoc=TableDoc(
doc='''
@@ -193,7 +194,9 @@
'cpu':
'The CPU this event was emitted on.',
'utid':
- 'The thread this event was emitted on.'
+ 'The thread this event was emitted on.',
+ 'common_flags':
+ 'Ftrace event flags for this event. Currently only emitted for sched_waking events.'
}))
FTRACE_EVENT_TABLE = Table(
diff --git a/src/trace_processor/tables/sched_tables.py b/src/trace_processor/tables/sched_tables.py
new file mode 100644
index 0000000..d65c8be
--- /dev/null
+++ b/src/trace_processor/tables/sched_tables.py
@@ -0,0 +1,166 @@
+# 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 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.
+"""Contains tables for relevant for sched."""
+
+from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import ColumnDoc
+from python.generators.trace_processor_table.public import ColumnFlag
+from python.generators.trace_processor_table.public import CppInt32
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import CppOptional
+from python.generators.trace_processor_table.public import CppSelfTableId
+from python.generators.trace_processor_table.public import CppString
+from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.public import TableDoc
+from python.generators.trace_processor_table.public import WrappingSqlView
+
+SCHED_SLICE_TABLE = Table(
+ python_module=__file__,
+ class_name='SchedSliceTable',
+ sql_name='sched_slice',
+ columns=[
+ C('ts', CppInt64(), flags=ColumnFlag.SORTED),
+ C('dur', CppInt64()),
+ C('cpu', CppUint32()),
+ C('utid', CppUint32()),
+ C('end_state', CppString()),
+ C('priority', CppInt32()),
+ ],
+ tabledoc=TableDoc(
+ doc='''
+ This table holds slices with kernel thread scheduling information.
+ These slices are collected when the Linux "ftrace" data source is
+ used with the "sched/switch" and "sched/wakeup*" events enabled.
+
+ The rows in this table will always have a matching row in the
+ |thread_state| table with |thread_state.state| = 'Running'
+ ''',
+ group='Events',
+ columns={
+ 'ts':
+ '''The timestamp at the start of the slice (in nanoseconds).''',
+ 'dur':
+ '''The duration of the slice (in nanoseconds).''',
+ 'utid':
+ '''The thread's unique id in the trace..''',
+ 'cpu':
+ '''The CPU that the slice executed on.''',
+ 'end_state':
+ '''
+ A string representing the scheduling state of the kernel
+ thread at the end of the slice. The individual characters in
+ the string mean the following: R (runnable), S (awaiting a
+ wakeup), D (in an uninterruptible sleep), T (suspended),
+ t (being traced), X (exiting), P (parked), W (waking),
+ I (idle), N (not contributing to the load average),
+ K (wakeable on fatal signals) and Z (zombie, awaiting
+ cleanup).
+ ''',
+ 'priority':
+ '''The kernel priority that the thread ran at.'''
+ }))
+
+SPURIOUS_SCHED_WAKEUP_TABLE = Table(
+ python_module=__file__,
+ class_name='SpuriousSchedWakeupTable',
+ sql_name='spurious_sched_wakeup',
+ columns=[
+ C('ts', CppInt64(), flags=ColumnFlag.SORTED),
+ C('thread_state_id', CppInt64()),
+ C('irq_context', CppOptional(CppUint32())),
+ C('utid', CppUint32()),
+ C('waker_utid', CppUint32()),
+ ],
+ tabledoc=TableDoc(
+ doc='''
+ This table contains the scheduling wakeups that occurred while a thread was
+ not blocked, i.e. running or runnable. Such wakeups are not tracked in the
+ |thread_state_table|.
+ ''',
+ group='Events',
+ columns={
+ 'ts':
+ 'The timestamp at the start of the slice (in nanoseconds).',
+ 'thread_state_id':
+ 'The id of the row in the thread_state table that this row is associated with.',
+ 'irq_context':
+ '''Whether the wakeup was from interrupt context or process context.''',
+ 'utid':
+ '''The thread's unique id in the trace..''',
+ 'waker_utid':
+ '''
+ The unique thread id of the thread which caused a wakeup of
+ this thread.
+ '''
+ }))
+
+THREAD_STATE_TABLE = Table(
+ python_module=__file__,
+ class_name='ThreadStateTable',
+ sql_name='thread_state',
+ columns=[
+ C('ts', CppInt64(), flags=ColumnFlag.SORTED),
+ C('dur', CppInt64()),
+ C('cpu', CppOptional(CppUint32())),
+ C('utid', CppUint32()),
+ C('state', CppString()),
+ C('io_wait', CppOptional(CppUint32())),
+ C('blocked_function', CppOptional(CppString())),
+ C('waker_utid', CppOptional(CppUint32())),
+ C('irq_context', CppOptional(CppUint32())),
+ ],
+ tabledoc=TableDoc(
+ doc='''
+ This table contains the scheduling state of every thread on the
+ system during the trace.
+
+ The rows in this table which have |state| = 'Running', will have a
+ corresponding row in the |sched_slice| table.
+ ''',
+ group='Events',
+ columns={
+ 'ts':
+ 'The timestamp at the start of the slice (in nanoseconds).',
+ 'dur':
+ 'The duration of the slice (in nanoseconds).',
+ 'cpu':
+ '''The CPU that the slice executed on.''',
+ 'irq_context':
+ '''Whether the wakeup was from interrupt context or process context.''',
+ 'utid':
+ '''The thread's unique id in the trace..''',
+ 'state':
+ '''
+ The scheduling state of the thread. Can be "Running" or any
+ of the states described in |sched_slice.end_state|.
+ ''',
+ 'io_wait':
+ 'Indicates whether this thread was blocked on IO.',
+ 'blocked_function':
+ 'The function in the kernel this thread was blocked on.',
+ 'waker_utid':
+ '''
+ The unique thread id of the thread which caused a wakeup of
+ this thread.
+ '''
+ }))
+
+# Keep this list sorted.
+ALL_TABLES = [
+ SCHED_SLICE_TABLE,
+ SPURIOUS_SCHED_WAKEUP_TABLE,
+ THREAD_STATE_TABLE,
+]
diff --git a/src/trace_processor/tables/slice_tables.py b/src/trace_processor/tables/slice_tables.py
index 8d85411..f1a4d90 100644
--- a/src/trace_processor/tables/slice_tables.py
+++ b/src/trace_processor/tables/slice_tables.py
@@ -120,100 +120,6 @@
''',
}))
-SCHED_SLICE_TABLE = Table(
- python_module=__file__,
- class_name='SchedSliceTable',
- sql_name='sched_slice',
- columns=[
- C('ts', CppInt64(), flags=ColumnFlag.SORTED),
- C('dur', CppInt64()),
- C('cpu', CppUint32()),
- C('utid', CppUint32()),
- C('end_state', CppString()),
- C('priority', CppInt32()),
- ],
- tabledoc=TableDoc(
- doc='''
- This table holds slices with kernel thread scheduling information.
- These slices are collected when the Linux "ftrace" data source is
- used with the "sched/switch" and "sched/wakeup*" events enabled.
-
- The rows in this table will always have a matching row in the
- |thread_state| table with |thread_state.state| = 'Running'
- ''',
- group='Events',
- columns={
- 'ts':
- '''The timestamp at the start of the slice (in nanoseconds).''',
- 'dur':
- '''The duration of the slice (in nanoseconds).''',
- 'utid':
- '''The thread's unique id in the trace..''',
- 'cpu':
- '''The CPU that the slice executed on.''',
- 'end_state':
- '''
- A string representing the scheduling state of the kernel
- thread at the end of the slice. The individual characters in
- the string mean the following: R (runnable), S (awaiting a
- wakeup), D (in an uninterruptible sleep), T (suspended),
- t (being traced), X (exiting), P (parked), W (waking),
- I (idle), N (not contributing to the load average),
- K (wakeable on fatal signals) and Z (zombie, awaiting
- cleanup).
- ''',
- 'priority':
- '''The kernel priority that the thread ran at.'''
- }))
-
-THREAD_STATE_TABLE = Table(
- python_module=__file__,
- class_name='ThreadStateTable',
- sql_name='thread_state',
- columns=[
- C('ts', CppInt64(), flags=ColumnFlag.SORTED),
- C('dur', CppInt64()),
- C('cpu', CppOptional(CppUint32())),
- C('utid', CppUint32()),
- C('state', CppString()),
- C('io_wait', CppOptional(CppUint32())),
- C('blocked_function', CppOptional(CppString())),
- C('waker_utid', CppOptional(CppUint32())),
- ],
- tabledoc=TableDoc(
- doc='''
- This table contains the scheduling state of every thread on the
- system during the trace.
-
- The rows in this table which have |state| = 'Running', will have a
- corresponding row in the |sched_slice| table.
- ''',
- group='Events',
- columns={
- 'ts':
- 'The timestamp at the start of the slice (in nanoseconds).',
- 'dur':
- 'The duration of the slice (in nanoseconds).',
- 'cpu':
- '''The CPU that the slice executed on.''',
- 'utid':
- '''The thread's unique id in the trace..''',
- 'state':
- '''
- The scheduling state of the thread. Can be "Running" or any
- of the states described in |sched_slice.end_state|.
- ''',
- 'io_wait':
- 'Indicates whether this thread was blocked on IO.',
- 'blocked_function':
- 'The function in the kernel this thread was blocked on.',
- 'waker_utid':
- '''
- The unique thread id of the thread which caused a wakeup of
- this thread.
- '''
- }))
-
GPU_SLICE_TABLE = Table(
python_module=__file__,
class_name='GpuSliceTable',
@@ -380,7 +286,5 @@
EXPERIMENTAL_FLAT_SLICE_TABLE,
GPU_SLICE_TABLE,
GRAPHICS_FRAME_SLICE_TABLE,
- SCHED_SLICE_TABLE,
SLICE_TABLE,
- THREAD_STATE_TABLE,
]
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 3dcf113..ba57b22 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -20,6 +20,7 @@
#include "src/trace_processor/tables/memory_tables_py.h"
#include "src/trace_processor/tables/metadata_tables_py.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/trace_proto_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
@@ -71,13 +72,16 @@
ProfilerSmapsTable::~ProfilerSmapsTable() = default;
GpuCounterGroupTable::~GpuCounterGroupTable() = default;
+// sched_tables_py.h
+SchedSliceTable::~SchedSliceTable() = default;
+SpuriousSchedWakeupTable::~SpuriousSchedWakeupTable() = default;
+ThreadStateTable::~ThreadStateTable() = default;
+
// slice_tables_py.h
SliceTable::~SliceTable() = default;
FlowTable::~FlowTable() = default;
-SchedSliceTable::~SchedSliceTable() = default;
GpuSliceTable::~GpuSliceTable() = default;
GraphicsFrameSliceTable::~GraphicsFrameSliceTable() = default;
-ThreadStateTable::~ThreadStateTable() = default;
ExpectedFrameTimelineSliceTable::~ExpectedFrameTimelineSliceTable() = default;
ActualFrameTimelineSliceTable::~ActualFrameTimelineSliceTable() = default;
ExperimentalFlatSliceTable::~ExperimentalFlatSliceTable() = default;
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index f64b1c1..c25595d 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -52,6 +52,7 @@
#include "src/trace_processor/prelude/functions/create_view_function.h"
#include "src/trace_processor/prelude/functions/import.h"
#include "src/trace_processor/prelude/functions/layout_functions.h"
+#include "src/trace_processor/prelude/functions/math.h"
#include "src/trace_processor/prelude/functions/pprof_functions.h"
#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/prelude/functions/sqlite3_str_split.h"
@@ -565,6 +566,11 @@
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
+ {
+ base::Status status = RegisterMathFunctions(engine_);
+ if (!status.ok())
+ PERFETTO_ELOG("%s", status.c_message());
+ }
const TraceStorage* storage = context_.storage.get();
@@ -650,6 +656,7 @@
RegisterDbTable(storage->flow_table());
RegisterDbTable(storage->slice_table());
RegisterDbTable(storage->sched_slice_table());
+ RegisterDbTable(storage->spurious_sched_wakeup_table());
RegisterDbTable(storage->thread_state_table());
RegisterDbTable(storage->gpu_slice_table());
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 0755e02..7fdc70c 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -3472,7 +3472,8 @@
"foo", perfetto::DynamicString{std::string("Event5")},
::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END);
PERFETTO_INTERNAL_TRACK_EVENT(
- "foo", "Event6", ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END);
+ "foo", perfetto::StaticString{"Event6"},
+ ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END);
auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
ASSERT_EQ(6u, slices.size());
diff --git a/test/data/chrome_5672_histograms.pftrace.gz.sha256 b/test/data/chrome_5672_histograms.pftrace.gz.sha256
new file mode 100644
index 0000000..5d22333
--- /dev/null
+++ b/test/data/chrome_5672_histograms.pftrace.gz.sha256
@@ -0,0 +1 @@
+a09bd44078ac71bcfbc901b0544750e8344d0d0f6f96e220f700a5a53fa932ee
\ No newline at end of file
diff --git a/test/data/sched_wakeup_trace.atr.sha256 b/test/data/sched_wakeup_trace.atr.sha256
new file mode 100644
index 0000000..496b259
--- /dev/null
+++ b/test/data/sched_wakeup_trace.atr.sha256
@@ -0,0 +1 @@
+ae61181ded60bf214859c2072b90dca49226338901d368e6aea329681bff30db
\ No newline at end of file
diff --git a/test/data/speedometer.perfetto_trace.gz.sha256 b/test/data/speedometer.perfetto_trace.gz.sha256
new file mode 100644
index 0000000..d330773
--- /dev/null
+++ b/test/data/speedometer.perfetto_trace.gz.sha256
@@ -0,0 +1 @@
+8a159b354d74a3ca0d38ce9cd071ef47de322db4261ee266bfafe04d70310529
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
index d75d44d..c4e2b2b 100644
--- a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
@@ -44,7 +44,7 @@
pid: 3000
}
ts: 2000000
- dur: 150000000
+ dur: 180000000
blocking_calls {
name: "Contending for pthread mutex"
cnt: 1
@@ -60,6 +60,20 @@
min_dur_ms: 10
}
blocking_calls {
+ name: "ExpNotRow#onMeasure(BigTextStyle)"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ blocking_calls {
+ name: "ExpNotRow#onMeasure(MessagingStyle)"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ blocking_calls {
name: "ImageDecoder#decodeBitmap"
cnt: 1
total_dur_ms: 10
@@ -88,6 +102,13 @@
min_dur_ms: 10
}
blocking_calls {
+ name: "NotificationStackScrollLayout#onMeasure"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ blocking_calls {
name: "SuspendThreadByThreadId <...>"
cnt: 1
total_dur_ms: 10
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
index 1039e47..7ec8cb9 100755
--- a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
@@ -26,11 +26,13 @@
# List of blocking calls
blocking_call_names = [
'monitor contention with something else', 'SuspendThreadByThreadId 123',
- 'LoadApkAssetsFd 123', 'binder transaction',
- 'inflate', 'Lock contention on thread list lock (owner tid: 1665)',
+ 'LoadApkAssetsFd 123', 'binder transaction', 'inflate',
+ 'Lock contention on thread list lock (owner tid: 1665)',
'CancellableContinuationImpl#123', 'relayoutWindow*', 'measure', 'layout',
'configChanged', 'Contending for pthread mutex',
'ImageDecoder#decodeBitmap', 'ImageDecoder#decodeDrawable',
+ 'NotificationStackScrollLayout#onMeasure',
+ 'ExpNotRow#onMeasure(MessagingStyle)', 'ExpNotRow#onMeasure(BigTextStyle)',
'Should not be in the metric'
]
diff --git a/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out b/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out
index dae2c44..b13137f 100644
--- a/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out
+++ b/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out
@@ -1,4 +1,3 @@
"scroll_processing_ms","scroll_jank_processing_ms","scroll_jank_percentage"
12374.560000,154.217000,1.246242
-
diff --git a/test/trace_processor/diff_tests/chrome/chrome_speedometer.out b/test/trace_processor/diff_tests/chrome/chrome_speedometer.out
new file mode 100644
index 0000000..2a52485
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_speedometer.out
@@ -0,0 +1,11 @@
+"iteration","ts","dur","total","mean","geomean","score","num_measurements"
+1,693997310311984,7020297000,5191976000,"324498500.0","254177934.4","78.7",96
+2,694004414619984,6308034000,4600497000,"287531062.5","224382472.1","89.1",96
+3,694010770005984,5878289000,4209484000,"263092750.0","200261720.2","99.9",96
+4,694016699502984,5934578000,4213632000,"263352000.0","201163561.9","99.4",96
+5,694022683560984,5952163000,4259111000,"266194437.5","203014932.4","98.5",96
+6,694028690570984,5966530000,4269728000,"266858000.0","204306068.2","97.9",96
+7,694034719276984,5853043000,4219351000,"263709437.5","200358118.8","99.8",96
+8,694040637173984,6087435000,4261576000,"266348500.0","202962356.2","98.5",96
+9,694046772284984,6040820000,4245060000,"265316250.0","199331263.1","100.3",96
+10,694052857814984,6063770000,4388487000,"274280437.5","208004176.2","96.2",96
diff --git a/test/trace_processor/diff_tests/chrome/chrome_speedometer_test.sql b/test/trace_processor/diff_tests/chrome/chrome_speedometer_test.sql
new file mode 100644
index 0000000..374d72e
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_speedometer_test.sql
@@ -0,0 +1,20 @@
+SELECT IMPORT('chrome.speedometer');
+
+SELECT
+ iteration,
+ ts,
+ dur,
+ total,
+ format('%.1f', mean) AS mean,
+ format('%.1f', geomean) AS geomean,
+ format('%.1f', score) AS score,
+ num_measurements
+FROM
+ chrome_speedometer_iteration,
+ (
+ SELECT iteration, COUNT(*) AS num_measurements
+ FROM chrome_speedometer_measure
+ GROUP BY iteration
+ )
+USING (iteration)
+ORDER BY iteration;
diff --git a/test/trace_processor/diff_tests/chrome/chrome_tasks.out b/test/trace_processor/diff_tests/chrome/chrome_tasks.out
index 7ca510e..7a75cec 100644
--- a/test/trace_processor/diff_tests/chrome/chrome_tasks.out
+++ b/test/trace_processor/diff_tests/chrome/chrome_tasks.out
@@ -1,5 +1,5 @@
-"full_name","task_type","count"
+"name","task_type","count"
"OnLibevent","other",2208
"RunTask(posted_from=components/favicon/core/large_icon_worker.cc:OnIconLookupComplete)","scheduler",694
"RunTask(posted_from=components/history/core/browser/history_service.cc:GetLargestFaviconForURL)","scheduler",694
@@ -50,3 +50,164 @@
"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:OpenOrCreateEntryInternal)","scheduler",34
"network.mojom.URLLoaderClient message (hash=2503424824)","mojo",32
"RunTask(posted_from=components/update_client/component.cc:TransitionState)","scheduler",31
+"RunTask(posted_from=net/http/http_stream_factory_job.cc:RunLoop)","scheduler",31
+"RunTask(posted_from=cc/tiles/tile_manager.cc:TaskSetFinished)","scheduler",30
+"RunTask(posted_from=gpu/command_buffer/service/scheduler.cc:TryScheduleSequence)","scheduler",29
+"viz.mojom.CompositorFrameSinkClient message (hash=50871626)","mojo",29
+"network.mojom.URLLoaderFactory message (hash=4026588969)","mojo",28
+"tracing.mojom.ProducerHost message (hash=1567334432)","mojo",27
+"RunTask(posted_from=services/service_manager/public/cpp/interface_binder.h:BindInterface)","scheduler",26
+"RunTask(posted_from=base/memory/memory_pressure_listener.cc:Notify)","scheduler",25
+"RunTask(posted_from=net/http/http_cache.cc:ProcessQueuedTransactions)","scheduler",25
+"Choreographer(java_views=ToolbarLayout,ToolbarPhone.updateLocationBarLayoutForExpansionAnimation)","choreographer",24
+"tracing.mojom.TracingSessionHost message (hash=167101205)","mojo",24
+"tracing.mojom.TracingSessionHost reply (hash=167101205)","mojo",24
+"RunTask(posted_from=components/history/core/browser/history_service.cc:ScheduleTask)","scheduler",23
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:DoomEntryInternal)","scheduler",23
+"RunTask(posted_from=components/omnibox/browser/history_url_provider.cc:ExecuteWithDB)","scheduler",21
+"content.mojom.ChildProcessHost message (hash=3455726814)","mojo",21
+"Looper.dispatch: android.app.ActivityThread$H(null)","other",20
+"RunTask(posted_from=cc/base/unique_notifier.cc:Schedule)","scheduler",20
+"RunTask(posted_from=ipc/ipc_mojo_bootstrap.cc:NotifyEndpointOfError)","scheduler",20
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/binder_map_internal.h:BindInterface)","scheduler",19
+"RunTask(posted_from=content/browser/gpu/gpu_process_host.cc:CallOnIO)","scheduler",18
+"RunTask(posted_from=services/metrics/public/cpp/delegating_ukm_recorder.cc:AddEntry)","scheduler",18
+"blink.mojom.BrowserInterfaceBroker message (hash=2708892102)","mojo",17
+"RunTask(posted_from=components/update_client/update_engine.cc:HandleComponentComplete)","scheduler",16
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:EntryOperationComplete)","scheduler",16
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:ReturnEntryToCallerAsync)","scheduler",16
+"tracing.mojom.ProducerClient message (hash=1884113734)","mojo",16
+"RunTask(posted_from=cc/tiles/tile_manager.cc:RunOnWorkerThread)","scheduler",15
+"RunTask(posted_from=components/update_client/component.cc:DoHandle)","scheduler",15
+"RunTask(posted_from=gpu/ipc/service/gpu_channel_manager.cc:ScheduleWakeUpGpu)","scheduler",15
+"RunTask(posted_from=third_party/blink/renderer/controller/memory_usage_monitor.cc:StartMonitoringIfNeeded)","scheduler",15
+"content.mojom.FrameHost message (hash=3826696652)","mojo",15
+"tracing.mojom.ProducerClient reply (hash=1377057286)","mojo",15
+"RunTask(posted_from=components/crash/content/browser/crash_metrics_reporter_android.cc:NotifyObservers)","scheduler",14
+"RunTask(posted_from=components/update_client/component.cc:EndState)","scheduler",14
+"RunTask(posted_from=components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc:PostTaskToClientThread)","scheduler",14
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:SendMessageWithResponder)","scheduler",14
+"RunTask(posted_from=third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc:PostTaskToMainThread)","scheduler",14
+"viz.mojom.GpuHost message (hash=1989238374)","mojo",14
+"RunTask(posted_from=base/allocator/partition_alloc_support.cc:RunMemoryReclaimer)","scheduler",13
+"RunTask(posted_from=cc/trees/proxy_main.cc:SendCommitRequestToImplThreadIfNeeded)","scheduler",13
+"RunTask(posted_from=components/viz/service/display/display_scheduler.cc:ScheduleBeginFrameDeadline)","scheduler",13
+"RunTask(posted_from=content/browser/webui/web_ui_url_loader_factory.cc:DataAvailable)","scheduler",13
+"RunTask(posted_from=gin/v8_platform.cc:PostJob)","scheduler",13
+"RunTask(posted_from=third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc:DeletePendingRequest)","scheduler",13
+"SingleThreadProxy::BeginMainFrame(java_views=)","ui_thread_begin_main_frame",13
+"blink.mojom.WidgetInputHandler message (hash=1243813610)","mojo",13
+"content.mojom.FrameHost message (hash=171518470)","mojo",13
+"tracing.mojom.ProducerClient message (hash=1377057286)","mojo",13
+"RunTask(posted_from=components/offline_pages/task/task_queue.cc:StartTaskIfAvailable)","scheduler",12
+"RunTask(posted_from=components/page_load_metrics/renderer/page_timing_metrics_sender.cc:EnsureSendTimer)","scheduler",12
+"RunTask(posted_from=content/browser/webui/web_ui_data_source_impl.cc:GetDataResourceBytesOnWorkerThread)","scheduler",12
+"RunTask(posted_from=net/spdy/spdy_session.cc:MaybePostWriteLoop)","scheduler",12
+"RunTask(posted_from=third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc:UpdatePolicyLocked)","scheduler",12
+"page_load_metrics.mojom.PageLoadMetrics message (hash=1589948206)","mojo",12
+"viz.mojom.CompositorFrameSink message (hash=1797539858)","mojo",12
+"viz.mojom.CompositorFrameSinkClient message (hash=713406245)","mojo",12
+"RunTask(posted_from=ipc/ipc_channel_mojo.cc:SendMessage)","scheduler",11
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:OnAssociatedInterfaceRequest)","scheduler",11
+"blink.mojom.LocalMainFrameHost message (hash=2681895571)","mojo",11
+"Looper.dispatch: android.view.ViewRootImpl$ViewRootHandler(android.widget.Editor$Blink@ef6aa9b)","other",10
+"RunTask(posted_from=components/leveldb_proto/internal/proto_database_impl.h:PostTransaction)","scheduler",10
+"RunTask(posted_from=components/leveldb_proto/internal/proto_leveldb_wrapper.cc:LoadEntriesWithFilter)","scheduler",10
+"RunTask(posted_from=gpu/ipc/service/gpu_channel.cc:CreateCommandBuffer)","scheduler",10
+"RunTask(posted_from=cc/trees/layer_tree_host_impl.cc:DidPresentCompositorFrame)","scheduler",9
+"RunTask(posted_from=content/browser/child_process_launcher_helper.cc:PostLaunchOnLauncherThread)","scheduler",9
+"RunTask(posted_from=content/browser/child_process_launcher_helper.cc:StartLaunchOnClientThread)","scheduler",9
+"RunTask(posted_from=content/browser/child_process_launcher_helper_android.cc:LaunchProcessOnLauncherThread)","scheduler",9
+"RunTask(posted_from=content/browser/tracing/background_tracing_manager_impl.cc:ActivateForProcess)","scheduler",9
+"RunTask(posted_from=mojo/core/node_controller.cc:SendBrokerClientInvitation)","scheduler",9
+"blink.mojom.Widget message (hash=1337806005)","mojo",9
+"content.mojom.ChildProcess message (hash=1868925865)","mojo",9
+"device.mojom.DeviceService message (hash=3372813913)","mojo",9
+"device.mojom.PowerMonitor message (hash=1428246654)","mojo",9
+"memory_instrumentation.mojom.CoordinatorConnector message (hash=3189782928)","mojo",9
+"tracing.mojom.PerfettoService message (hash=3821615380)","mojo",9
+"tracing.mojom.TracedProcess reply (hash=2621133919)","mojo",9
+"tracing.mojom.TracingService message (hash=2713683366)","mojo",9
+"ChildProcessConnection.ChildServiceConnection.onServiceConnected","java",8
+"Looper.dispatch: android.net.ConnectivityManager$CallbackHandler(null)","other",8
+"RunTask(posted_from=base/allocator/partition_alloc_support.cc:RunThreadCachePeriodicPurge)","scheduler",8
+"RunTask(posted_from=chrome/browser/data_saver/data_saver.cc:FetchDataSaverOSSettingAsynchronously)","scheduler",8
+"RunTask(posted_from=gpu/command_buffer/service/gr_cache_controller.cc:ScheduleGrContextCleanup)","scheduler",8
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:AddFilter)","scheduler",8
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/connector.cc:PostDispatchNextMessageFromPipe)","mojo",8
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/connector.cc:StartReceiving)","scheduler",8
+"RunTask(posted_from=net/cert/multi_threaded_cert_verifier.cc:Start)","scheduler",8
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:PostClientCallback)","scheduler",8
+"RunTask(posted_from=ui/events/gesture_detection/gesture_detector.cc:StartTimeout)","scheduler",8
+"blink.mojom.LocalFrameHost message (hash=3716466459)","mojo",8
+"content.mojom.FrameHost message (hash=4063347880)","mojo",8
+"tracing.mojom.ProducerClient message (hash=3526922743)","mojo",8
+"RunTask(posted_from=base/threading/thread.cc:StopSoon)","scheduler",7
+"RunTask(posted_from=components/crash/content/browser/child_process_crash_observer_android.cc:OnChildExit)","scheduler",7
+"RunTask(posted_from=components/viz/service/display_embedder/skia_output_device.cc:FinishSwapBuffers)","scheduler",7
+"RunTask(posted_from=components/viz/service/gl/gpu_service_impl.cc:GetPeakMemoryUsage)","scheduler",7
+"RunTask(posted_from=components/viz/service/gl/gpu_service_impl.cc:GetPeakMemoryUsageOnMainThread)","scheduler",7
+"RunTask(posted_from=components/viz/service/gl/gpu_service_impl.cc:StartPeakMemoryMonitor)","scheduler",7
+"RunTask(posted_from=content/browser/browser_child_process_host_impl.cc:RegisterCoordinatorClient)","scheduler",7
+"RunTask(posted_from=content/browser/service_process_host_impl.cc:Launch)","scheduler",7
+"RunTask(posted_from=content/utility/utility_thread_impl.cc:BindServiceInterface)","scheduler",7
+"RunTask(posted_from=third_party/blink/renderer/core/frame/web_frame_widget_impl.cc:DidSwap)","scheduler",7
+"RunTask(posted_from=third_party/blink/renderer/core/script/module_map.cc:DispatchFinishedNotificationAsync)","scheduler",7
+"RunTask(posted_from=ui/gfx/android/android_surface_control_compat.cc:SetOnCommitCb)","scheduler",7
+"RunTask(posted_from=ui/gfx/android/android_surface_control_compat.cc:SetOnCompleteCb)","scheduler",7
+"content.mojom.ChildProcessHost message (hash=38682745)","mojo",7
+"media_session.mojom.MediaSessionObserver message (hash=3966185760)","mojo",7
+"viz.mojom.GpuService reply (hash=3002350734)","mojo",7
+"ChromeApplication.attachBaseContext","java",6
+"RunTask(posted_from=base/files/important_file_writer.cc:WriteNowWithBackgroundDataProducer)","scheduler",6
+"RunTask(posted_from=base/power_monitor/power_monitor.cc:NotifyPowerStateChange)","scheduler",6
+"RunTask(posted_from=base/task/sequenced_task_runner.h:operator())","scheduler",6
+"RunTask(posted_from=cc/trees/proxy_impl.cc:DrawInternal)","scheduler",6
+"RunTask(posted_from=cc/trees/proxy_impl.cc:~ScopedCommitCompletionEvent)","scheduler",6
+"RunTask(posted_from=cc/trees/single_thread_proxy.cc:DidReceiveCompositorFrameAckOnImplThread)","scheduler",6
+"RunTask(posted_from=chrome/browser/image_decoder/image_decoder.cc:RunDecodeCallbackOnTaskRunner)","scheduler",6
+"RunTask(posted_from=chrome/browser/image_decoder/image_decoder.cc:StartWithOptionsImpl)","scheduler",6
+"RunTask(posted_from=components/history/core/browser/history_service.cc:GetFaviconsForURL)","scheduler",6
+"RunTask(posted_from=components/leveldb_proto/internal/proto_leveldb_wrapper.cc:UpdateEntries)","scheduler",6
+"RunTask(posted_from=components/offline_pages/task/task_queue.cc:TaskCompletedCallback)","scheduler",6
+"RunTask(posted_from=components/omnibox/browser/autocomplete_controller.cc:StartStopTimer)","scheduler",6
+"RunTask(posted_from=components/performance_manager/decorators/page_load_tracker_decorator_helper.cc:NotifyPageLoadTrackerDecoratorOnPMSequence)","scheduler",6
+"RunTask(posted_from=components/performance_manager/performance_manager_impl.cc:CreateNodeImpl)","scheduler",6
+"RunTask(posted_from=components/power_scheduler/power_mode_arbiter.cc:OnTaskRunnerAvailable)","scheduler",6
+"RunTask(posted_from=content/browser/service_worker/service_worker_context_core.cc:NotifyClientIsExecutionReady)","scheduler",6
+"RunTask(posted_from=content/child/child_thread_impl.cc:ExposeInterfacesToBrowser)","scheduler",6
+"RunTask(posted_from=content/child/child_thread_impl.cc:GetBackgroundTracingAgentProvider)","scheduler",6
+"RunTask(posted_from=content/child/child_thread_impl.cc:Init)","scheduler",6
+"RunTask(posted_from=content/common/android/cpu_time_metrics_internal.cc:ProcessCpuTimeMetrics)","scheduler",6
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:Init)","scheduler",6
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:OnChannelConnected)","scheduler",6
+"RunTask(posted_from=mojo/core/node_controller.cc:AcceptBrokerClientInvitation)","scheduler",6
+"RunTask(posted_from=mojo/core/node_controller.cc:Create)","scheduler",6
+"RunTask(posted_from=services/device/public/cpp/power_monitor/power_monitor_broadcast_source.cc:Init)","scheduler",6
+"RunTask(posted_from=services/tracing/public/cpp/perfetto/perfetto_traced_process.cc:CreateProducerConnection)","scheduler",6
+"RunTask(posted_from=services/tracing/public/cpp/perfetto/producer_client.cc:Connect)","scheduler",6
+"RunTask(posted_from=third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc:UpdateForInputEventOnCompositorThread)","scheduler",6
+"blink.mojom.PageBroadcast message (hash=1682824955)","mojo",6
+"network.mojom.NetworkService message (hash=121963099)","mojo",6
+"service_manager.mojom.InterfaceProvider message (hash=607834802)","mojo",6
+"tracing.mojom.ProducerClient message (hash=2964700609)","mojo",6
+"tracing.mojom.TracedProcess message (hash=2621133919)","mojo",6
+"Choreographer(java_views=ToolbarLayout,ToolbarPhone.layoutLocationBar,ToolbarPhone.updateLocationBarLayoutForExpansionAnimation)","choreographer",5
+"Looper.dispatch: android.os.Handler(ZM3@26e2110)","other",5
+"RunTask(posted_from=base/files/important_file_writer.cc:ScheduleWrite)","scheduler",5
+"RunTask(posted_from=cc/trees/proxy_main.cc:UpdateBrowserControlsState)","scheduler",5
+"RunTask(posted_from=components/leveldb_proto/internal/proto_database_impl.h:ParseLoadedEntries)","scheduler",5
+"RunTask(posted_from=components/viz/common/gpu/context_cache_controller.cc:PostIdleCallback)","scheduler",5
+"RunTask(posted_from=gin/v8_foreground_task_runner.cc:PostNonNestableTask)","scheduler",5
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:Pause)","scheduler",5
+"RunTask(posted_from=net/socket/transport_client_socket_pool.cc:StartBackupJobTimer)","scheduler",5
+"RunTask(posted_from=third_party/blink/renderer/core/css/font_face_set.cc:HandlePendingEventsAndPromisesSoon)","scheduler",5
+"RunTask(posted_from=ui/gl/gl_surface_egl_surface_control.cc:CheckPendingPresentationCallbacks)","scheduler",5
+"blink.mojom.LocalFrameHost message (hash=3721282066)","mojo",5
+"blink.mojom.LocalMainFrame message (hash=2603884585)","mojo",5
+"blink.mojom.LocalMainFrameHost message (hash=2936437485)","mojo",5
+"content.mojom.ChildHistogramFetcherFactory message (hash=3650055636)","mojo",5
+"media_session.mojom.MediaSessionObserver message (hash=169447862)","mojo",5
+"network.mojom.DevToolsObserver message (hash=3093371228)","mojo",5
+"viz.mojom.GpuHost message (hash=2261948180)","mojo",5
+"viz.mojom.GpuHost message (hash=4264056690)","mojo",5
diff --git a/test/trace_processor/diff_tests/chrome/tests.py b/test/trace_processor/diff_tests/chrome/tests.py
index ec4181e..c6187aa 100644
--- a/test/trace_processor/diff_tests/chrome/tests.py
+++ b/test/trace_processor/diff_tests/chrome/tests.py
@@ -253,11 +253,11 @@
query="""
SELECT RUN_METRIC('chrome/chrome_tasks.sql');
- SELECT full_name, task_type, count() AS count
+ SELECT full_name as name, task_type, count() AS count
FROM chrome_tasks
GROUP BY full_name, task_type
- ORDER BY count DESC
- LIMIT 50;
+ HAVING count >= 5
+ ORDER BY count DESC, name;
""",
out=Path('chrome_tasks.out'))
@@ -446,6 +446,45 @@
"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
"""))
+ # Chrome custom navigation event names
+ def test_chrome_histograms(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_5672_histograms.pftrace.gz'),
+ query="""
+ SELECT IMPORT('chrome.histograms');
+
+ SELECT
+ name,
+ count() as count
+ FROM chrome_histograms
+ GROUP BY name
+ ORDER BY count DESC, name
+ LIMIT 20;
+ """,
+ out=Csv("""
+ "name","count"
+ "Net.QuicSession.AsyncRead",19207
+ "Net.QuicSession.NumQueuedPacketsBeforeWrite",19193
+ "RendererScheduler.QueueingDuration.NormalPriority",9110
+ "Net.OnTransferSizeUpdated.Experimental.OverridenBy",8525
+ "Compositing.Renderer.AnimationUpdateOnMissingPropertyNode",3489
+ "Net.QuicConnection.WritePacketStatus",3099
+ "Net.QuicSession.PacketWriteTime.Synchronous",3082
+ "Net.QuicSession.SendPacketSize.ForwardSecure",3012
+ "Net.URLLoaderThrottleExecutionTime.WillStartRequest",1789
+ "Net.URLLoaderThrottleExecutionTime.BeforeWillProcessResponse",1773
+ "Net.URLLoaderThrottleExecutionTime.WillProcessResponse",1773
+ "UMA.StackProfiler.SampleInOrder",1534
+ "GPU.SharedImage.ContentConsumed",1037
+ "Gpu.Rasterization.Raster.MSAASampleCountLog2",825
+ "Scheduling.Renderer.DeadlineMode",637
+ "Blink.CullRect.UpdateTime",622
+ "Scheduling.Renderer.BeginImplFrameLatency2",591
+ "Net.QuicSession.CoalesceStreamFrameStatus",551
+ "API.StorageAccess.AllowedRequests2",541
+ "Net.HttpResponseCode",541
+ """))
+
# Trace proto content
def test_proto_content(self):
return DiffTestBlueprint(
@@ -453,6 +492,12 @@
query=Path('proto_content_test.sql'),
out=Path('proto_content.out'))
+ def test_speedometer(self):
+ return DiffTestBlueprint(
+ trace=DataPath('speedometer.perfetto_trace.gz'),
+ query=Path('chrome_speedometer_test.sql'),
+ out=Path('chrome_speedometer.out'))
+
# TODO(mayzner): Uncomment when it works
# def test_proto_content_path(self):
# return DiffTestBlueprint(
diff --git a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
index c8594d1..ef0f373 100644
--- a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
@@ -541,17 +541,16 @@
5680,120000000,70000000,120000000,-1
"""))
- # TODO(283531332): reenable this test after fixing.
- # def test_chrome_scroll_jank_v2(self):
- # return DiffTestBlueprint(
- # trace=Path('../../data/event_latency_with_args.perfetto-trace'),
- # query="""
- # SELECT RUN_METRIC('chrome/chrome_scroll_jank_v2.sql');
+ def test_chrome_scroll_jank_v2(self):
+ return DiffTestBlueprint(
+ trace=DataPath('event_latency_with_args.perfetto-trace'),
+ query="""
+ SELECT RUN_METRIC('chrome/chrome_scroll_jank_v2.sql');
- # SELECT
- # scroll_processing_ms,
- # scroll_jank_processing_ms,
- # scroll_jank_percentage
- # FROM chrome_scroll_jank_v2_output;
- # """,
- # out=Path('chrome_scroll_jank_v2.out'))
+ SELECT
+ scroll_processing_ms,
+ scroll_jank_processing_ms,
+ scroll_jank_percentage
+ FROM chrome_scroll_jank_v2;
+ """,
+ out=Path('chrome_scroll_jank_v2.out'))
diff --git a/test/trace_processor/diff_tests/functions/tests.py b/test/trace_processor/diff_tests/functions/tests.py
index 7f952ed..e5dc15c 100644
--- a/test/trace_processor/diff_tests/functions/tests.py
+++ b/test/trace_processor/diff_tests/functions/tests.py
@@ -520,3 +520,21 @@
4,3
5,3
"""))
+
+ def test_math_functions(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT
+ CAST(EXP(1) * 1000 AS INTEGER) AS a,
+ CAST(LN(1) * 1000 AS INTEGER) AS b,
+ CAST(LN(EXP(1)) * 1000 AS INTEGER) AS c,
+ EXP("asd") AS d,
+ EXP(NULL) AS e,
+ LN("as") AS f,
+ LN(NULL) AS g
+ """,
+ out=Csv("""
+ "a","b","c","d","e","f","g"
+ 2718,0,1000,"[NULL]","[NULL]","[NULL]","[NULL]"
+ """))
diff --git a/test/trace_processor/diff_tests/tables/tests_sched.py b/test/trace_processor/diff_tests/tables/tests_sched.py
index 1f1ee49..3ef8d14 100644
--- a/test/trace_processor/diff_tests/tables/tests_sched.py
+++ b/test/trace_processor/diff_tests/tables/tests_sched.py
@@ -79,3 +79,44 @@
81473010341386
81473010352792
"""))
+
+ def test_sched_wakeup(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT * FROM spurious_sched_wakeup
+ ORDER BY ts LIMIT 10
+ """,
+ out=Csv("""
+ "id","type","ts","thread_state_id","irq_context","utid","waker_utid"
+ 0,"spurious_sched_wakeup",1735850782904,395,0,230,1465
+ 1,"spurious_sched_wakeup",1736413914899,852,0,230,1467
+ 2,"spurious_sched_wakeup",1736977755745,1261,0,230,1469
+ 3,"spurious_sched_wakeup",1737046900004,1434,0,1472,1473
+ 4,"spurious_sched_wakeup",1737047159060,1463,0,1474,1472
+ 5,"spurious_sched_wakeup",1737081636170,2721,0,1214,1319
+ 6,"spurious_sched_wakeup",1737108696536,4684,0,501,557
+ 7,"spurious_sched_wakeup",1737153309978,6080,0,11,506
+ 8,"spurious_sched_wakeup",1737165240546,6562,0,565,499
+ 9,"spurious_sched_wakeup",1737211563344,8645,0,178,1195
+ """))
+
+ def test_raw_common_flags(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT * FROM raw WHERE common_flags != 0 ORDER BY ts LIMIT 10
+ """,
+ out=Csv("""
+ "id","type","ts","name","cpu","utid","arg_set_id","common_flags"
+ 3,"ftrace_event",1735489788930,"sched_waking",0,300,4,1
+ 4,"ftrace_event",1735489812571,"sched_waking",0,300,5,1
+ 5,"ftrace_event",1735489833977,"sched_waking",1,305,6,1
+ 8,"ftrace_event",1735489876788,"sched_waking",1,297,9,1
+ 9,"ftrace_event",1735489879097,"sched_waking",0,304,10,1
+ 12,"ftrace_event",1735489933912,"sched_waking",0,428,13,1
+ 14,"ftrace_event",1735489972385,"sched_waking",1,232,15,1
+ 17,"ftrace_event",1735489999987,"sched_waking",1,232,15,1
+ 19,"ftrace_event",1735490039439,"sched_waking",1,298,18,1
+ 20,"ftrace_event",1735490042084,"sched_waking",1,298,19,1
+ """))