Merge "Replace nbsp with space in queries." into main
diff --git a/Android.bp b/Android.bp
index b78249c..8e65554 100644
--- a/Android.bp
+++ b/Android.bp
@@ -10358,10 +10358,10 @@
name: "perfetto_src_trace_processor_perfetto_sql_engine_engine",
srcs: [
"src/trace_processor/perfetto_sql/engine/created_function.cc",
- "src/trace_processor/perfetto_sql/engine/created_table_function.cc",
"src/trace_processor/perfetto_sql/engine/function_util.cc",
"src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc",
"src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc",
+ "src/trace_processor/perfetto_sql/engine/runtime_table_function.cc",
],
}
@@ -10427,7 +10427,7 @@
filegroup {
name: "perfetto_src_trace_processor_perfetto_sql_intrinsics_table_functions_interface",
srcs: [
- "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.cc",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.cc",
],
}
diff --git a/BUILD b/BUILD
index a28f7f3..70af987 100644
--- a/BUILD
+++ b/BUILD
@@ -2104,14 +2104,14 @@
srcs = [
"src/trace_processor/perfetto_sql/engine/created_function.cc",
"src/trace_processor/perfetto_sql/engine/created_function.h",
- "src/trace_processor/perfetto_sql/engine/created_table_function.cc",
- "src/trace_processor/perfetto_sql/engine/created_table_function.h",
"src/trace_processor/perfetto_sql/engine/function_util.cc",
"src/trace_processor/perfetto_sql/engine/function_util.h",
"src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc",
"src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h",
"src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc",
"src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h",
+ "src/trace_processor/perfetto_sql/engine/runtime_table_function.cc",
+ "src/trace_processor/perfetto_sql/engine/runtime_table_function.h",
],
)
@@ -2167,8 +2167,8 @@
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_intrinsics_table_functions_interface",
srcs = [
- "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.cc",
- "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.cc",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h",
],
)
diff --git a/docs/contributing/ui-plugins.md b/docs/contributing/ui-plugins.md
new file mode 100644
index 0000000..7a674e8
--- /dev/null
+++ b/docs/contributing/ui-plugins.md
@@ -0,0 +1,81 @@
+# UI plugins
+The Perfetto UI can be extended with plugins. These plugins are shipped
+part of Perfetto.
+
+## Create a plugin
+The guide below explains how to create a plugin for the Perfetto UI.
+
+### Prepare for UI development
+First we need to prepare the UI development environment.
+You will need to use a MacOS or Linux machine.
+Follow the steps below or see the
+[Getting Started](./getting-started) guide for more detail.
+
+```sh
+git clone https://android.googlesource.com/platform/external/perfetto/
+cd perfetto
+./tool/install-build-deps --ui
+```
+
+### Copy the plugin skeleton
+```sh
+cp -r ui/plugins/com.example.Skeleton ui/plugins/<your-plugin-name>
+```
+Now edit `ui/plugins/<your-plugin-name>/index.ts`.
+Search for all instances of `SKELETON: <instruction>` in the file and
+follow the instructions.
+
+Notes on naming:
+- Don't name the directory `XyzPlugin` just `Xyz`.
+- The `pluginId` and directory name must match.
+- Plugins should be prefixed with the reversed components of a domain
+ name you control. For example if `example.com` is your domain your
+ plugin should be named `com.example.Foo`.
+- Core plugins maintained by the Perfetto team should use
+ `dev.perfetto.Foo`.
+
+### Start the dev server
+```sh
+./ui/run-dev-server
+```
+Now navigate to [](http://localhost:10000/settings)
+
+### Upload your plugin for review
+- Update `ui/plugins/<your-plugin-name>/OWNERS` to include your email.
+- Follow the [Contributing](./getting-started#contributing)
+ instructions to upload your CL to the codereview tool.
+- Once uploaded add `hjd@google.com` as a reviewer for your CL.
+
+## Plugin extension points
+Plugins can extend a handful of specific places in the UI. The sections
+below show these extension points and give examples of how they can be
+used.
+
+### Commands
+TBD
+
+### Tracks
+TBD
+
+### Detail tabs
+TBD
+
+### Metric Visualisations
+TBD
+
+## Guide to the plugin API
+TBD
+
+## Default plugins
+TBD
+
+## Misc notes
+- Plugins must be licensed under
+ [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html)
+ the same as all other code in the repository.
+- Plugins are the responsibility of the OWNERS of that plugin to
+ maintain, not the responsibility of the Perfetto team. All
+ efforts will be made to keep the plugin API stable and existing
+ plugins working however plugins that remain unmaintained for long
+ periods of time will be disabled and ultimately deleted.
+
diff --git a/docs/faq.md b/docs/faq.md
index df41ff3..89491ec 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -20,7 +20,7 @@
tools/open_trace_in_ui -i /path/to/trace
```
-## Why does Perfetto not support <some obscure JSON format feature>?
+## Why does Perfetto not support \<some obscure JSON format feature\>?
The JSON trace format is considered a legacy trace format and is supported on a
best-effort basis. While we try our best to maintain compatibility with the
diff --git a/docs/toc.md b/docs/toc.md
index 6fe3ae9..b0d8fc1 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -75,11 +75,12 @@
* [Getting started](contributing/getting-started.md)
* [Build instructions](contributing/build-instructions.md)
* [Running tests](contributing/testing.md)
- * [Common tasks](contributing/common-tasks.md)
+ * [UI plugins](contributing/ui-plugins.md)
+ * [UI development hints](contributing/ui-development.md)
* [Embedding Perfetto](contributing/embedding.md)
* [Releasing the SDK](contributing/sdk-releasing.md)
* [Chrome branches](contributing/chrome-branches.md)
- * [UI development](contributing/ui-development.md)
+ * [Common tasks](contributing/common-tasks.md)
* [Press](contributing/perfetto-in-the-press.md)
* [Design documents](#)
diff --git a/examples/sdk/README.md b/examples/sdk/README.md
index 3519006..21b64e2 100644
--- a/examples/sdk/README.md
+++ b/examples/sdk/README.md
@@ -67,6 +67,10 @@
```bash
export NDK=/path/to/ndk
cmake -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
+ -DANDROID_ABI=arm64-v8a \
+ -DANDROID_PLATFORM=android-21 \
+ -DANDROID_LD=lld \
+ -DCMAKE_BUILD_TYPE=Release \
-B build_android
cmake --build build_android
```
diff --git a/examples/sdk/example_system_wide.cc b/examples/sdk/example_system_wide.cc
index 87fb7a5..177b38b 100644
--- a/examples/sdk/example_system_wide.cc
+++ b/examples/sdk/example_system_wide.cc
@@ -91,6 +91,7 @@
// are going to use the system-wide tracing service, so that we can see our
// app's events in context with system profiling information.
args.backends = perfetto::kSystemBackend;
+ args.enable_system_consumer = false;
perfetto::Tracing::Initialize(args);
perfetto::TrackEvent::Register();
diff --git a/examples/shared_lib/example_shlib_data_source.c b/examples/shared_lib/example_shlib_data_source.c
index f9e4040..d8aa1f7 100644
--- a/examples/shared_lib/example_shlib_data_source.c
+++ b/examples/shared_lib/example_shlib_data_source.c
@@ -29,7 +29,7 @@
PerfettoProducerInit(args);
PerfettoDsRegister(&custom, "com.example.custom_data_source",
- PerfettoDsNoCallbacks());
+ PerfettoDsParamsDefault());
for (;;) {
PERFETTO_DS_TRACE(custom, ctx) {
diff --git a/include/perfetto/public/abi/data_source_abi.h b/include/perfetto/public/abi/data_source_abi.h
index 9eae031..a3a60aa 100644
--- a/include/perfetto/public/abi/data_source_abi.h
+++ b/include/perfetto/public/abi/data_source_abi.h
@@ -172,6 +172,27 @@
PERFETTO_SDK_EXPORT void PerfettoDsSetCbUserArg(struct PerfettoDsImpl*,
void* user_arg);
+enum PerfettoDsBufferExhaustedPolicy {
+ // If the data source runs out of space when trying to acquire a new chunk,
+ // it will drop data.
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP = 0,
+ // If the data source runs out of space when trying to acquire a new chunk,
+ // it will stall, retry and eventually abort if a free chunk is not acquired
+ // after a while.
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT = 1,
+};
+
+// If the data source doesn't find an empty chunk when trying to emit tracing
+// data, it will behave according to `policy` (which is a `enum
+// PerfettoDsBufferExhaustedPolicy`).
+//
+// Should not be called after PerfettoDsImplRegister().
+//
+// Returns true if successful, false otherwise.
+PERFETTO_SDK_EXPORT bool PerfettoDsSetBufferExhaustedPolicy(
+ struct PerfettoDsImpl*,
+ uint32_t policy);
+
// Registers the `*ds_impl` data source type.
//
// `ds_impl` must be obtained via a call to `PerfettoDsImplCreate()`.
diff --git a/include/perfetto/public/abi/producer_abi.h b/include/perfetto/public/abi/producer_abi.h
index 94dc134..a939b73 100644
--- a/include/perfetto/public/abi/producer_abi.h
+++ b/include/perfetto/public/abi/producer_abi.h
@@ -31,6 +31,19 @@
// Initializes the global in-process perfetto producer.
PERFETTO_SDK_EXPORT void PerfettoProducerInProcessInit(void);
+// Informs the tracing services to activate any of these triggers if any tracing
+// session was waiting for them.
+//
+// `trigger_names` is an array of `const char*` (zero terminated strings). The
+// last pointer in the array must be NULL.
+//
+// Sends the trigger signal to all the initialized backends that are currently
+// connected and that connect in the next `ttl_ms` milliseconds (but
+// returns immediately anyway).
+PERFETTO_SDK_EXPORT void PerfettoProducerActivateTriggers(
+ const char* trigger_names[],
+ uint32_t ttl_ms);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/perfetto/public/abi/tracing_session_abi.h b/include/perfetto/public/abi/tracing_session_abi.h
index 113ac9b..4446bb8 100644
--- a/include/perfetto/public/abi/tracing_session_abi.h
+++ b/include/perfetto/public/abi/tracing_session_abi.h
@@ -41,6 +41,15 @@
void* cfg_begin,
size_t cfg_len);
+typedef void (*PerfettoTracingSessionStopCb)(struct PerfettoTracingSessionImpl*,
+ void* user_arg);
+
+// Calls `*cb` with `user_arg` when the tracing session is stopped.
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionSetStopCb(
+ struct PerfettoTracingSessionImpl*,
+ PerfettoTracingSessionStopCb cb,
+ void* user_arg);
+
PERFETTO_SDK_EXPORT void PerfettoTracingSessionStartAsync(
struct PerfettoTracingSessionImpl*);
diff --git a/include/perfetto/public/data_source.h b/include/perfetto/public/data_source.h
index b244df1..65f1256 100644
--- a/include/perfetto/public/data_source.h
+++ b/include/perfetto/public/data_source.h
@@ -22,9 +22,11 @@
#include "perfetto/public/abi/atomic.h"
#include "perfetto/public/abi/data_source_abi.h"
+#include "perfetto/public/abi/heap_buffer.h"
#include "perfetto/public/compiler.h"
#include "perfetto/public/pb_msg.h"
#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/protos/common/data_source_descriptor.pzc.h"
#include "perfetto/public/protos/trace/trace_packet.pzc.h"
// A data source type.
@@ -40,7 +42,7 @@
{ &perfetto_atomic_false, PERFETTO_NULL }
// All the callbacks are optional and can be NULL if not needed.
-struct PerfettoDsCallbacks {
+struct PerfettoDsParams {
// Instance lifecycle callbacks:
PerfettoDsOnSetupCb on_setup_cb;
PerfettoDsOnStartCb on_start_cb;
@@ -60,75 +62,87 @@
// Passed to all the callbacks as the `user_arg` param.
void* user_arg;
+
+ // How to behave when running out of shared memory buffer space.
+ enum PerfettoDsBufferExhaustedPolicy buffer_exhausted_policy;
};
-static inline struct PerfettoDsCallbacks PerfettoDsNoCallbacks(void) {
- struct PerfettoDsCallbacks ret = {
- PERFETTO_NULL, PERFETTO_NULL, PERFETTO_NULL, PERFETTO_NULL, PERFETTO_NULL,
- PERFETTO_NULL, PERFETTO_NULL, PERFETTO_NULL, PERFETTO_NULL};
+static inline struct PerfettoDsParams PerfettoDsParamsDefault(void) {
+ struct PerfettoDsParams ret = {
+ PERFETTO_NULL, PERFETTO_NULL,
+ PERFETTO_NULL, PERFETTO_NULL,
+ PERFETTO_NULL, PERFETTO_NULL,
+ PERFETTO_NULL, PERFETTO_NULL,
+ PERFETTO_NULL, PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP};
return ret;
}
// Registers the data source type `ds`, named `data_source_name` with the global
// perfetto producer.
-//
-// `callbacks` are called when certain events happen on the data source type.
-// PerfettoDsNoCallbacks() can be used if callbacks are not needed.
-//
-// TODO(ddiproietto): Accept the full DataSourceDescriptor, not just the
-// data_source_name
static inline bool PerfettoDsRegister(struct PerfettoDs* ds,
const char* data_source_name,
- struct PerfettoDsCallbacks callbacks) {
+ struct PerfettoDsParams params) {
struct PerfettoDsImpl* ds_impl;
bool success;
- // Build the DataSourceDescriptor protobuf message.
- size_t data_source_name_len = strlen(data_source_name);
- uint8_t* data_source_desc = PERFETTO_STATIC_CAST(
- uint8_t*, malloc(data_source_name_len + PERFETTO_PB_VARINT_MAX_SIZE_32 +
- PERFETTO_PB_VARINT_MAX_SIZE_64));
- uint8_t* write_ptr = data_source_desc;
- const int32_t name_field_id = 1; // perfetto.protos.DataSourceDescriptor.name
- write_ptr = PerfettoPbWriteVarInt(
- PerfettoPbMakeTag(name_field_id, PERFETTO_PB_WIRE_TYPE_DELIMITED),
- write_ptr);
- write_ptr = PerfettoPbWriteVarInt(data_source_name_len, write_ptr);
- memcpy(write_ptr, data_source_name, data_source_name_len);
- write_ptr += data_source_name_len;
+ void* desc_buf;
+ size_t desc_size;
+
+ ds->enabled = &perfetto_atomic_false;
+ ds->impl = PERFETTO_NULL;
+
+ {
+ struct PerfettoPbMsgWriter writer;
+ struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer);
+ struct perfetto_protos_DataSourceDescriptor desc;
+ PerfettoPbMsgInit(&desc.msg, &writer);
+
+ perfetto_protos_DataSourceDescriptor_set_cstr_name(&desc, data_source_name);
+
+ desc_size = PerfettoStreamWriterGetWrittenSize(&writer.writer);
+ desc_buf = malloc(desc_size);
+ PerfettoHeapBufferCopyInto(hb, &writer.writer, desc_buf, desc_size);
+ PerfettoHeapBufferDestroy(hb, &writer.writer);
+ }
+
+ if (!desc_buf) {
+ return false;
+ }
ds_impl = PerfettoDsImplCreate();
- if (callbacks.on_setup_cb) {
- PerfettoDsSetOnSetupCallback(ds_impl, callbacks.on_setup_cb);
+ if (params.on_setup_cb) {
+ PerfettoDsSetOnSetupCallback(ds_impl, params.on_setup_cb);
}
- if (callbacks.on_start_cb) {
- PerfettoDsSetOnStartCallback(ds_impl, callbacks.on_start_cb);
+ if (params.on_start_cb) {
+ PerfettoDsSetOnStartCallback(ds_impl, params.on_start_cb);
}
- if (callbacks.on_stop_cb) {
- PerfettoDsSetOnStopCallback(ds_impl, callbacks.on_stop_cb);
+ if (params.on_stop_cb) {
+ PerfettoDsSetOnStopCallback(ds_impl, params.on_stop_cb);
}
- if (callbacks.on_flush_cb) {
- PerfettoDsSetOnFlushCallback(ds_impl, callbacks.on_flush_cb);
+ if (params.on_flush_cb) {
+ PerfettoDsSetOnFlushCallback(ds_impl, params.on_flush_cb);
}
- if (callbacks.on_create_tls_cb) {
- PerfettoDsSetOnCreateTls(ds_impl, callbacks.on_create_tls_cb);
+ if (params.on_create_tls_cb) {
+ PerfettoDsSetOnCreateTls(ds_impl, params.on_create_tls_cb);
}
- if (callbacks.on_delete_tls_cb) {
- PerfettoDsSetOnDeleteTls(ds_impl, callbacks.on_delete_tls_cb);
+ if (params.on_delete_tls_cb) {
+ PerfettoDsSetOnDeleteTls(ds_impl, params.on_delete_tls_cb);
}
- if (callbacks.on_create_incr_cb) {
- PerfettoDsSetOnCreateIncr(ds_impl, callbacks.on_create_incr_cb);
+ if (params.on_create_incr_cb) {
+ PerfettoDsSetOnCreateIncr(ds_impl, params.on_create_incr_cb);
}
- if (callbacks.on_delete_incr_cb) {
- PerfettoDsSetOnDeleteIncr(ds_impl, callbacks.on_delete_incr_cb);
+ if (params.on_delete_incr_cb) {
+ PerfettoDsSetOnDeleteIncr(ds_impl, params.on_delete_incr_cb);
}
- if (callbacks.user_arg) {
- PerfettoDsSetCbUserArg(ds_impl, callbacks.user_arg);
+ if (params.user_arg) {
+ PerfettoDsSetCbUserArg(ds_impl, params.user_arg);
+ }
+ if (params.buffer_exhausted_policy !=
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) {
+ PerfettoDsSetBufferExhaustedPolicy(ds_impl, params.buffer_exhausted_policy);
}
- success = PerfettoDsImplRegister(
- ds_impl, &ds->enabled, data_source_desc,
- PERFETTO_STATIC_CAST(size_t, write_ptr - data_source_desc));
- free(data_source_desc);
+ success = PerfettoDsImplRegister(ds_impl, &ds->enabled, desc_buf, desc_size);
+ free(desc_buf);
if (!success) {
return false;
}
diff --git a/include/perfetto/public/producer.h b/include/perfetto/public/producer.h
index 4545baa..56692c6 100644
--- a/include/perfetto/public/producer.h
+++ b/include/perfetto/public/producer.h
@@ -17,8 +17,11 @@
#ifndef INCLUDE_PERFETTO_PUBLIC_PRODUCER_H_
#define INCLUDE_PERFETTO_PUBLIC_PRODUCER_H_
+#include <stdint.h>
+
#include "perfetto/public/abi/backend_type.h"
#include "perfetto/public/abi/producer_abi.h"
+#include "perfetto/public/compiler.h"
// Arguments for PerfettoProducerInit. This struct is not ABI-stable, fields can
// be added and rearranged.
@@ -37,4 +40,18 @@
}
}
+// Informs the tracing services to activate the single trigger `trigger_name` if
+// any tracing session was waiting for it.
+//
+// Sends the trigger signal to all the initialized backends that are currently
+// connected and that connect in the next `ttl_ms` milliseconds (but
+// returns immediately anyway).
+static inline void PerfettoProducerActivateTrigger(const char* trigger_name,
+ uint32_t ttl_ms) {
+ const char* trigger_names[2];
+ trigger_names[0] = trigger_name;
+ trigger_names[1] = PERFETTO_NULL;
+ PerfettoProducerActivateTriggers(trigger_names, ttl_ms);
+}
+
#endif // INCLUDE_PERFETTO_PUBLIC_PRODUCER_H_
diff --git a/include/perfetto/public/protos/BUILD.gn b/include/perfetto/public/protos/BUILD.gn
index 5901a27..64d324f 100644
--- a/include/perfetto/public/protos/BUILD.gn
+++ b/include/perfetto/public/protos/BUILD.gn
@@ -25,5 +25,6 @@
"trace/track_event/debug_annotation.pzc.h",
"trace/track_event/track_descriptor.pzc.h",
"trace/track_event/track_event.pzc.h",
+ "trace/trigger.pzc.h",
]
}
diff --git a/include/perfetto/public/protos/common/data_source_descriptor.pzc.h b/include/perfetto/public/protos/common/data_source_descriptor.pzc.h
new file mode 100644
index 0000000..21e62fb
--- /dev/null
+++ b/include/perfetto/public/protos/common/data_source_descriptor.pzc.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+// Autogenerated by the ProtoZero C compiler plugin.
+// Invoked by tools/gen_c_protos
+// DO NOT EDIT.
+#ifndef INCLUDE_PERFETTO_PUBLIC_PROTOS_COMMON_DATA_SOURCE_DESCRIPTOR_PZC_H_
+#define INCLUDE_PERFETTO_PUBLIC_PROTOS_COMMON_DATA_SOURCE_DESCRIPTOR_PZC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perfetto/public/pb_macros.h"
+
+PERFETTO_PB_MSG_DECL(perfetto_protos_FtraceDescriptor);
+PERFETTO_PB_MSG_DECL(perfetto_protos_GpuCounterDescriptor);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TrackEventDescriptor);
+
+PERFETTO_PB_MSG(perfetto_protos_DataSourceDescriptor);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ STRING,
+ const char*,
+ name,
+ 1);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ VARINT,
+ uint64_t,
+ id,
+ 7);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ VARINT,
+ bool,
+ will_notify_on_stop,
+ 2);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ VARINT,
+ bool,
+ will_notify_on_start,
+ 3);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ VARINT,
+ bool,
+ handles_incremental_state_clear,
+ 4);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ MSG,
+ perfetto_protos_GpuCounterDescriptor,
+ gpu_counter_descriptor,
+ 5);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ MSG,
+ perfetto_protos_TrackEventDescriptor,
+ track_event_descriptor,
+ 6);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceDescriptor,
+ MSG,
+ perfetto_protos_FtraceDescriptor,
+ ftrace_descriptor,
+ 8);
+
+#endif // INCLUDE_PERFETTO_PUBLIC_PROTOS_COMMON_DATA_SOURCE_DESCRIPTOR_PZC_H_
diff --git a/include/perfetto/public/protos/trace/trigger.pzc.h b/include/perfetto/public/protos/trace/trigger.pzc.h
new file mode 100644
index 0000000..1ad6749
--- /dev/null
+++ b/include/perfetto/public/protos/trace/trigger.pzc.h
@@ -0,0 +1,42 @@
+/*
+ * 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 INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TRIGGER_PZC_H_
+#define INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TRIGGER_PZC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perfetto/public/pb_macros.h"
+
+PERFETTO_PB_MSG(perfetto_protos_Trigger);
+PERFETTO_PB_FIELD(perfetto_protos_Trigger,
+ STRING,
+ const char*,
+ trigger_name,
+ 1);
+PERFETTO_PB_FIELD(perfetto_protos_Trigger,
+ STRING,
+ const char*,
+ producer_name,
+ 2);
+PERFETTO_PB_FIELD(perfetto_protos_Trigger,
+ VARINT,
+ int32_t,
+ trusted_producer_uid,
+ 3);
+
+#endif // INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TRIGGER_PZC_H_
diff --git a/infra/ci/controller/controller.py b/infra/ci/controller/controller.py
index 7a3f79a..fed3b01 100644
--- a/infra/ci/controller/controller.py
+++ b/infra/ci/controller/controller.py
@@ -339,10 +339,10 @@
for (job_id, status) in failed_jobs.iteritems()
])
if passed_jobs:
- msg += 'PASS:\n'
+ msg += '#\nPASS:\n'
msg += ''.join(['- %s/%s\n' % (log_url, job_id) for job_id in passed_jobs])
if ui_links:
- msg += 'Artifacts:\n' + ''.join('- %s\n' % link for link in ui_links)
+ msg += '\nArtifacts:\n' + ''.join('- %s\n' % link for link in ui_links)
msg += 'CI page for this CL:\n'
msg += '- https://ci.perfetto.dev/#!/cls/%s\n' % cl_and_ps.split('-')[0]
body = {'labels': {}, 'message': msg}
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 474cb98..f0801da 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -750,6 +750,7 @@
TASK_TYPE_LOW_PRIORITY_SCRIPT_EXECUTION = 81;
TASK_TYPE_STORAGE = 82;
TASK_TYPE_NETWORKING_UNFREEZABLE_IMAGE_LOADING = 83;
+ TASK_TYPE_MAIN_THREAD_TASK_QUEUE_V8_LOW_PRIORITY = 84;
}
enum FrameType {
@@ -806,6 +807,9 @@
// This is same as LatencyInfo's trace_id, using the name event_latency_id to
// move away from the generic trace_id name used at other places as well.
optional int64 event_latency_id = 4;
+ // This is set only for scroll updates and is based on the
+ // Event.ScrollJank.DelayedFramesPercentage.FixedWindow metric.
+ optional bool is_janky_scrolled_frame = 5;
}
message ProcessSingleton {
@@ -950,6 +954,8 @@
UI_BEFORE_UNLOAD_BROWSER_RESPONSE_TQ = 54;
IO_BEFORE_UNLOAD_BROWSER_RESPONSE_TQ = 55;
+
+ V8_LOW_PRIORITY_TQ = 56;
}
optional Priority priority = 1;
diff --git a/python/generators/diff_tests/testing.py b/python/generators/diff_tests/testing.py
index 2e42b52..89aea8a 100644
--- a/python/generators/diff_tests/testing.py
+++ b/python/generators/diff_tests/testing.py
@@ -219,3 +219,20 @@
for method in methods
if method.__name__.startswith('test_')
]
+
+def PrintProfileProto(profile):
+ locations = {l.id: l for l in profile.location}
+ functions = {f.id: f for f in profile.function}
+ samples = []
+ for s in profile.sample:
+ stack = []
+ for location in [locations[id] for id in s.location_id]:
+ for function in [functions[l.function_id] for l in location.line]:
+ stack.append("{name} ({address})".format(
+ name=profile.string_table[function.name],
+ address=hex(location.address)))
+ if len(location.line) == 0:
+ stack.append("({address})".format(address=hex(location.address)))
+ samples.append('Sample:\nValues: {values}\nStack:\n{stack}'.format(
+ values=', '.join(map(str, s.value)), stack='\n'.join(stack)))
+ return '\n\n'.join(sorted(samples)) + '\n'
diff --git a/python/tools/check_imports.py b/python/tools/check_imports.py
index b4a1a32..8313702 100755
--- a/python/tools/check_imports.py
+++ b/python/tools/check_imports.py
@@ -144,16 +144,21 @@
r'/controller/.*',
'trying to reduce the dependency mess as we refactor into core',
),
-
- # Fails at the moment due to:
- # ui/src/base/comparison_utils.ts
- # -> ui/src/common/query_result.ts
- # -> ui/src/core/static_initializers.ts
- #NoDep(
- # r'/base/.*',
- # r'/core/.*',
- # 'core should depend on base not the other way round',
- #),
+ NoDep(
+ r'/base/.*',
+ r'/core/.*',
+ 'core should depend on base not the other way round',
+ ),
+ NoDep(
+ r'/base/.*',
+ r'/common/.*',
+ 'common should depend on base not the other way round',
+ ),
+ NoDep(
+ r'/common/.*',
+ r'/chrome_extension/.*',
+ 'chrome_extension must be a leaf',
+ ),
# Fails at the moment as we have several circular dependencies. One
# example:
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index e8db554..89ca409 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -233,14 +233,13 @@
deps += [ "//third_party/fuchsia-sdk/sdk/pkg/fdio" ]
}
if (perfetto_build_standalone || perfetto_build_with_android) {
+ sources += [ "unix_socket_unittest.cc" ]
+ deps += [ ":unix_socket" ]
+
# This causes some problems on the chromium waterfall.
if (is_linux || is_android) {
sources += [ "watchdog_unittest.cc" ]
}
- if (!is_win) {
- sources += [ "unix_socket_unittest.cc" ]
- deps += [ ":unix_socket" ]
- }
}
}
diff --git a/src/shared_lib/data_source.cc b/src/shared_lib/data_source.cc
index 1482f56..1e46c32 100644
--- a/src/shared_lib/data_source.cc
+++ b/src/shared_lib/data_source.cc
@@ -18,6 +18,7 @@
#include <bitset>
+#include "perfetto/tracing/buffer_exhausted_policy.h"
#include "perfetto/tracing/data_source.h"
#include "perfetto/tracing/internal/basic_types.h"
#include "protos/perfetto/common/data_source_descriptor.gen.h"
@@ -60,6 +61,9 @@
// Passed to all the callbacks as the `user_arg` param.
void* cb_user_arg;
+ perfetto::BufferExhaustedPolicy buffer_exhausted_policy =
+ perfetto::BufferExhaustedPolicy::kDrop;
+
DataSourceType cpp_type;
std::atomic<bool> enabled{false};
std::mutex mu;
@@ -262,6 +266,24 @@
ds_impl->cb_user_arg = user_arg;
}
+bool PerfettoDsSetBufferExhaustedPolicy(struct PerfettoDsImpl* ds_impl,
+ uint32_t policy) {
+ if (ds_impl->IsRegistered()) {
+ return false;
+ }
+
+ switch (policy) {
+ case PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP:
+ ds_impl->buffer_exhausted_policy = perfetto::BufferExhaustedPolicy::kDrop;
+ return true;
+ case PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT:
+ ds_impl->buffer_exhausted_policy =
+ perfetto::BufferExhaustedPolicy::kStall;
+ return true;
+ }
+ return false;
+}
+
bool PerfettoDsImplRegister(struct PerfettoDsImpl* ds_impl,
PERFETTO_ATOMIC(bool) * *enabled_ptr,
const void* descriptor,
@@ -295,7 +317,7 @@
params.supports_multiple_instances = true;
params.requires_callbacks_under_lock = false;
bool success = data_source_type->cpp_type.Register(
- dsd, factory, params, perfetto::BufferExhaustedPolicy::kDrop,
+ dsd, factory, params, data_source_type->buffer_exhausted_policy,
create_custom_tls_fn, create_incremental_state_fn, cb_ctx);
if (!success) {
return false;
diff --git a/src/shared_lib/producer.cc b/src/shared_lib/producer.cc
index e4991b8..ebd28b2 100644
--- a/src/shared_lib/producer.cc
+++ b/src/shared_lib/producer.cc
@@ -46,3 +46,12 @@
args.backends = perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
}
+
+void PerfettoProducerActivateTriggers(const char* trigger_names[],
+ uint32_t ttl_ms) {
+ std::vector<std::string> triggers;
+ for (size_t i = 0; trigger_names[i] != nullptr; i++) {
+ triggers.push_back(trigger_names[i]);
+ }
+ perfetto::Tracing::ActivateTriggers(triggers, ttl_ms);
+}
diff --git a/src/shared_lib/test/api_integrationtest.cc b/src/shared_lib/test/api_integrationtest.cc
index 96ce34c..3ef111e 100644
--- a/src/shared_lib/test/api_integrationtest.cc
+++ b/src/shared_lib/test/api_integrationtest.cc
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-#include <condition_variable>
-#include <mutex>
#include <thread>
#include "perfetto/public/abi/data_source_abi.h"
+#include "perfetto/public/abi/heap_buffer.h"
#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/abi/tracing_session_abi.h"
#include "perfetto/public/data_source.h"
#include "perfetto/public/pb_decoder.h"
#include "perfetto/public/producer.h"
+#include "perfetto/public/protos/config/data_source_config.pzc.h"
+#include "perfetto/public/protos/config/trace_config.pzc.h"
#include "perfetto/public/protos/trace/test_event.pzc.h"
#include "perfetto/public/protos/trace/trace.pzc.h"
#include "perfetto/public/protos/trace/trace_packet.pzc.h"
+#include "perfetto/public/protos/trace/trigger.pzc.h"
#include "test/gtest_and_gmock.h"
@@ -188,7 +191,7 @@
args.backends = PERFETTO_BACKEND_IN_PROCESS;
PerfettoProducerInit(args);
PerfettoDsRegister(&data_source_1, kDataSourceName1,
- PerfettoDsNoCallbacks());
+ PerfettoDsParamsDefault());
RegisterDataSource2();
}
@@ -208,36 +211,36 @@
};
void RegisterDataSource2() {
- static struct PerfettoDsCallbacks callbacks = {};
- callbacks.on_setup_cb = [](struct PerfettoDsImpl* ds_impl,
- PerfettoDsInstanceIndex inst_id, void* ds_config,
- size_t ds_config_size, void* user_arg) -> void* {
+ struct PerfettoDsParams params = PerfettoDsParamsDefault();
+ params.on_setup_cb = [](struct PerfettoDsImpl* ds_impl,
+ PerfettoDsInstanceIndex inst_id, void* ds_config,
+ size_t ds_config_size, void* user_arg) -> void* {
auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
return thiz->ds2_callbacks_.OnSetup(ds_impl, inst_id, ds_config,
ds_config_size, thiz->ds2_user_arg_);
};
- callbacks.on_start_cb = [](struct PerfettoDsImpl* ds_impl,
- PerfettoDsInstanceIndex inst_id, void* user_arg,
- void* inst_ctx) -> void {
+ params.on_start_cb = [](struct PerfettoDsImpl* ds_impl,
+ PerfettoDsInstanceIndex inst_id, void* user_arg,
+ void* inst_ctx) -> void {
auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
return thiz->ds2_callbacks_.OnStart(ds_impl, inst_id, thiz->ds2_user_arg_,
inst_ctx);
};
- callbacks.on_stop_cb =
+ params.on_stop_cb =
[](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
void* user_arg, void* inst_ctx, struct PerfettoDsOnStopArgs* args) {
auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
return thiz->ds2_callbacks_.OnStop(
ds_impl, inst_id, thiz->ds2_user_arg_, inst_ctx, args);
};
- callbacks.on_flush_cb =
+ params.on_flush_cb =
[](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
void* user_arg, void* inst_ctx, struct PerfettoDsOnFlushArgs* args) {
auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
return thiz->ds2_callbacks_.OnFlush(
ds_impl, inst_id, thiz->ds2_user_arg_, inst_ctx, args);
};
- callbacks.on_create_tls_cb =
+ params.on_create_tls_cb =
[](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
@@ -247,12 +250,12 @@
thiz->ds2_user_arg_);
return state;
};
- callbacks.on_delete_tls_cb = [](void* ptr) {
+ params.on_delete_tls_cb = [](void* ptr) {
auto* state = static_cast<Ds2CustomState*>(ptr);
state->thiz->ds2_callbacks_.OnDeleteTls(state->actual);
delete state;
};
- callbacks.on_create_incr_cb =
+ params.on_create_incr_cb =
[](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg);
@@ -262,13 +265,13 @@
ds_impl, inst_id, tracer, thiz->ds2_user_arg_);
return state;
};
- callbacks.on_delete_incr_cb = [](void* ptr) {
+ params.on_delete_incr_cb = [](void* ptr) {
auto* state = static_cast<Ds2CustomState*>(ptr);
state->thiz->ds2_callbacks_.OnDeleteIncr(state->actual);
delete state;
};
- callbacks.user_arg = this;
- PerfettoDsRegister(&data_source_2, kDataSourceName2, callbacks);
+ params.user_arg = this;
+ PerfettoDsRegister(&data_source_2, kDataSourceName2, params);
}
void* Ds2ActualCustomState(void* ptr) {
@@ -558,4 +561,77 @@
PERFETTO_DS_TRACE(data_source_1, ctx) {}
}
+class SharedLibProducerTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ struct PerfettoProducerInitArgs args = {0};
+ args.backends = PERFETTO_BACKEND_IN_PROCESS;
+ PerfettoProducerInit(args);
+ }
+
+ void TearDown() override { perfetto::shlib::ResetForTesting(); }
+};
+
+TEST_F(SharedLibDataSourceTest, ActivateTriggers) {
+ struct PerfettoPbMsgWriter writer;
+ struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer);
+
+ struct perfetto_protos_TraceConfig cfg;
+ PerfettoPbMsgInit(&cfg.msg, &writer);
+ {
+ struct perfetto_protos_TraceConfig_BufferConfig buffers;
+ perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers);
+ perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024);
+ perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers);
+ }
+ {
+ struct perfetto_protos_TraceConfig_TriggerConfig trigger_config;
+ perfetto_protos_TraceConfig_begin_trigger_config(&cfg, &trigger_config);
+ perfetto_protos_TraceConfig_TriggerConfig_set_trigger_mode(
+ &trigger_config,
+ perfetto_protos_TraceConfig_TriggerConfig_STOP_TRACING);
+ perfetto_protos_TraceConfig_TriggerConfig_set_trigger_timeout_ms(
+ &trigger_config, 5000);
+ {
+ struct perfetto_protos_TraceConfig_TriggerConfig_Trigger trigger;
+ perfetto_protos_TraceConfig_TriggerConfig_begin_triggers(&trigger_config,
+ &trigger);
+ perfetto_protos_TraceConfig_TriggerConfig_Trigger_set_cstr_name(
+ &trigger, "trigger1");
+ perfetto_protos_TraceConfig_TriggerConfig_end_triggers(&trigger_config,
+ &trigger);
+ }
+ perfetto_protos_TraceConfig_end_trigger_config(&cfg, &trigger_config);
+ }
+ size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer);
+ std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]);
+ PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size);
+ PerfettoHeapBufferDestroy(hb, &writer.writer);
+
+ struct PerfettoTracingSessionImpl* ts =
+ PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS);
+
+ PerfettoTracingSessionSetup(ts, ser.get(), cfg_size);
+
+ PerfettoTracingSessionStartBlocking(ts);
+ TracingSession tracing_session = TracingSession::Adopt(ts);
+
+ const char* triggers[3];
+ triggers[0] = "trigger0";
+ triggers[1] = "trigger1";
+ triggers[2] = nullptr;
+ PerfettoProducerActivateTriggers(triggers, 10000);
+
+ tracing_session.WaitForStopped();
+ std::vector<uint8_t> data = tracing_session.ReadBlocking();
+ EXPECT_THAT(FieldView(data),
+ Contains(PbField(
+ perfetto_protos_Trace_packet_field_number,
+ MsgField(Contains(PbField(
+ perfetto_protos_TracePacket_trigger_field_number,
+ MsgField(Contains(PbField(
+ perfetto_protos_Trigger_trigger_name_field_number,
+ StringField("trigger1"))))))))));
+}
+
} // namespace
diff --git a/src/shared_lib/test/benchmark.cc b/src/shared_lib/test/benchmark.cc
index b30e86f..4861a31 100644
--- a/src/shared_lib/test/benchmark.cc
+++ b/src/shared_lib/test/benchmark.cc
@@ -45,7 +45,7 @@
struct PerfettoProducerInitArgs args = {0};
args.backends = PERFETTO_BACKEND_IN_PROCESS;
PerfettoProducerInit(args);
- PerfettoDsRegister(&custom, kDataSourceName, PerfettoDsNoCallbacks());
+ PerfettoDsRegister(&custom, kDataSourceName, PerfettoDsParamsDefault());
return true;
}
diff --git a/src/shared_lib/test/utils.cc b/src/shared_lib/test/utils.cc
index a479078..69013bd 100644
--- a/src/shared_lib/test/utils.cc
+++ b/src/shared_lib/test/utils.cc
@@ -85,22 +85,37 @@
PerfettoTracingSessionStartBlocking(ts);
+ return TracingSession::Adopt(ts);
+}
+
+TracingSession TracingSession::Adopt(
+ struct PerfettoTracingSessionImpl* session) {
TracingSession ret;
- ret.session_ = ts;
+ ret.session_ = session;
+ ret.stopped_ = std::make_unique<WaitableEvent>();
+ PerfettoTracingSessionSetStopCb(
+ ret.session_,
+ [](struct PerfettoTracingSessionImpl*, void* arg) {
+ static_cast<WaitableEvent*>(arg)->Notify();
+ },
+ ret.stopped_.get());
return ret;
}
TracingSession::TracingSession(TracingSession&& other) noexcept {
session_ = other.session_;
other.session_ = nullptr;
+ stopped_ = std::move(other.stopped_);
+ other.stopped_ = nullptr;
}
TracingSession::~TracingSession() {
if (!session_) {
return;
}
- if (!stopped_) {
+ if (!stopped_->IsNotified()) {
PerfettoTracingSessionStopBlocking(session_);
+ stopped_->WaitForNotification();
}
PerfettoTracingSessionDestroy(session_);
}
@@ -124,8 +139,11 @@
return result;
}
+void TracingSession::WaitForStopped() {
+ stopped_->WaitForNotification();
+}
+
void TracingSession::StopBlocking() {
- stopped_ = true;
PerfettoTracingSessionStopBlocking(session_);
}
diff --git a/src/shared_lib/test/utils.h b/src/shared_lib/test/utils.h
index 5197b19..875162f 100644
--- a/src/shared_lib/test/utils.h
+++ b/src/shared_lib/test/utils.h
@@ -22,6 +22,7 @@
#include <cstdint>
#include <functional>
#include <iterator>
+#include <memory>
#include <mutex>
#include <ostream>
#include <string>
@@ -78,6 +79,9 @@
private:
std::string data_source_name_;
};
+
+ static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
+
TracingSession(TracingSession&&) noexcept;
~TracingSession();
@@ -85,13 +89,14 @@
struct PerfettoTracingSessionImpl* session() const { return session_; }
bool FlushBlocking(uint32_t timeout_ms);
+ void WaitForStopped();
void StopBlocking();
std::vector<uint8_t> ReadBlocking();
private:
TracingSession() = default;
struct PerfettoTracingSessionImpl* session_;
- bool stopped_ = false;
+ std::unique_ptr<WaitableEvent> stopped_;
};
template <typename FieldSkipper>
diff --git a/src/shared_lib/tracing_session.cc b/src/shared_lib/tracing_session.cc
index 8a8ee82..28872e6 100644
--- a/src/shared_lib/tracing_session.cc
+++ b/src/shared_lib/tracing_session.cc
@@ -45,6 +45,13 @@
ts->Setup(cfg);
}
+void PerfettoTracingSessionSetStopCb(struct PerfettoTracingSessionImpl* session,
+ PerfettoTracingSessionStopCb cb,
+ void* user_arg) {
+ auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+ ts->SetOnStopCallback([session, cb, user_arg]() { cb(session, user_arg); });
+}
+
void PerfettoTracingSessionStartAsync(
struct PerfettoTracingSessionImpl* session) {
auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
diff --git a/src/trace_processor/perfetto_sql/engine/BUILD.gn b/src/trace_processor/perfetto_sql/engine/BUILD.gn
index 1e8950c..29d355f 100644
--- a/src/trace_processor/perfetto_sql/engine/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/engine/BUILD.gn
@@ -20,14 +20,14 @@
sources = [
"created_function.cc",
"created_function.h",
- "created_table_function.cc",
- "created_table_function.h",
"function_util.cc",
"function_util.h",
"perfetto_sql_engine.cc",
"perfetto_sql_engine.h",
"perfetto_sql_parser.cc",
"perfetto_sql_parser.h",
+ "runtime_table_function.cc",
+ "runtime_table_function.h",
]
deps = [
"../..:metatrace",
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
index c2de3e3..dcb99fe 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
@@ -26,9 +26,9 @@
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
#include "src/trace_processor/perfetto_sql/engine/created_function.h"
-#include "src/trace_processor/perfetto_sql/engine/created_table_function.h"
#include "src/trace_processor/perfetto_sql/engine/function_util.h"
#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h"
+#include "src/trace_processor/perfetto_sql/engine/runtime_table_function.h"
#include "src/trace_processor/sqlite/db_sqlite_table.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/sqlite/sql_source.h"
@@ -93,8 +93,8 @@
PerfettoSqlEngine::PerfettoSqlEngine(StringPool* pool)
: query_cache_(new QueryCache()), pool_(pool), engine_(new SqliteEngine()) {
- engine_->RegisterVirtualTableModule<CreatedTableFunction>(
- "created_table_function", this, SqliteTable::TableType::kExplicitCreate,
+ engine_->RegisterVirtualTableModule<RuntimeTableFunction>(
+ "runtime_table_function", this, SqliteTable::TableType::kExplicitCreate,
false);
}
@@ -102,11 +102,11 @@
// Destroying the sqlite engine should also destroy all the created table
// functions.
engine_.reset();
- PERFETTO_CHECK(created_table_function_state_.size() == 0);
+ PERFETTO_CHECK(runtime_table_fn_states_.size() == 0);
}
-void PerfettoSqlEngine::RegisterTable(const Table& table,
- const std::string& table_name) {
+void PerfettoSqlEngine::RegisterStaticTable(const Table& table,
+ const std::string& table_name) {
DbSqliteTable::Context context{
query_cache_.get(), DbSqliteTable::TableComputation::kStatic, &table,
/*sql_table=*/nullptr, /*generator=*/nullptr};
@@ -127,8 +127,8 @@
}
}
-void PerfettoSqlEngine::RegisterTableFunction(
- std::unique_ptr<TableFunction> fn) {
+void PerfettoSqlEngine::RegisterStaticTableFunction(
+ std::unique_ptr<StaticTableFunction> fn) {
std::string table_name = fn->TableName();
DbSqliteTable::Context context{
query_cache_.get(), DbSqliteTable::TableComputation::kTableFunction,
@@ -183,7 +183,7 @@
} else if (auto* cst = std::get_if<PerfettoSqlParser::CreateTable>(
&parser.statement())) {
RETURN_IF_ERROR(AddTracebackIfNeeded(
- RegisterSqlTable(cst->name, cst->sql), cst->sql));
+ RegisterRuntimeTable(cst->name, cst->sql), cst->sql));
// Since the rest of the code requires a statement, just use a no-value
// dummy statement.
source = cst->sql.FullRewrite(
@@ -298,8 +298,8 @@
std::move(*opt_return_type), std::move(return_type_str), std::move(sql));
}
-base::Status PerfettoSqlEngine::RegisterSqlTable(std::string name,
- SqlSource sql) {
+base::Status PerfettoSqlEngine::RegisterRuntimeTable(std::string name,
+ SqlSource sql) {
auto stmt_or = engine_->PrepareStatement(sql);
RETURN_IF_ERROR(stmt_or.status());
SqliteEngine::PreparedStatement stmt = std::move(stmt_or);
@@ -389,7 +389,7 @@
SqlSource::FromTraceProcessorImplementation("SELECT 0 WHERE 0"));
}
- CreatedTableFunction::State state{cf.prototype, cf.sql, {}, {}, std::nullopt};
+ RuntimeTableFunction::State state{cf.prototype, cf.sql, {}, {}, std::nullopt};
base::StringView function_name;
RETURN_IF_ERROR(
ParseFunctionName(state.prototype_str.c_str(), function_name));
@@ -480,7 +480,7 @@
std::string fn_name = state.prototype.function_name;
std::string lower_name = base::ToLower(state.prototype.function_name);
- if (created_table_function_state_.Find(lower_name)) {
+ if (runtime_table_fn_states_.Find(lower_name)) {
if (!cf.replace) {
return base::ErrStatus("Table function named %s already exists",
state.prototype.function_name.c_str());
@@ -493,26 +493,27 @@
RETURN_IF_ERROR(res.status());
}
- auto it_and_inserted = created_table_function_state_.Insert(
+ auto it_and_inserted = runtime_table_fn_states_.Insert(
lower_name,
- std::make_unique<CreatedTableFunction::State>(std::move(state)));
+ std::make_unique<RuntimeTableFunction::State>(std::move(state)));
PERFETTO_CHECK(it_and_inserted.second);
base::StackString<1024> create(
- "CREATE VIRTUAL TABLE %s USING created_table_function", fn_name.c_str());
+ "CREATE VIRTUAL TABLE %s USING runtime_table_function", fn_name.c_str());
return cf.sql.FullRewrite(
SqlSource::FromTraceProcessorImplementation(create.ToStdString()));
}
-CreatedTableFunction::State* PerfettoSqlEngine::GetTableFunctionState(
+RuntimeTableFunction::State* PerfettoSqlEngine::GetRuntimeTableFunctionState(
const std::string& name) const {
- auto it = created_table_function_state_.Find(base::ToLower(name));
+ auto it = runtime_table_fn_states_.Find(base::ToLower(name));
PERFETTO_CHECK(it);
return it->get();
}
-void PerfettoSqlEngine::OnTableFunctionDestroyed(const std::string& name) {
- PERFETTO_CHECK(created_table_function_state_.Erase(base::ToLower(name)));
+void PerfettoSqlEngine::OnRuntimeTableFunctionDestroyed(
+ const std::string& name) {
+ PERFETTO_CHECK(runtime_table_fn_states_.Erase(base::ToLower(name)));
}
} // namespace trace_processor
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
index 9a9c3aa..4cf867a 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
@@ -23,10 +23,10 @@
#include "perfetto/base/status.h"
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/perfetto_sql/engine/created_table_function.h"
#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h"
+#include "src/trace_processor/perfetto_sql/engine/runtime_table_function.h"
#include "src/trace_processor/perfetto_sql/intrinsics/functions/sql_function.h"
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/sqlite/sql_source.h"
#include "src/trace_processor/sqlite/sqlite_engine.h"
@@ -113,16 +113,17 @@
// Registers a trace processor C++ table with SQLite with an SQL name of
// |name|.
- void RegisterTable(const Table& table, const std::string& name);
+ void RegisterStaticTable(const Table& table, const std::string& name);
// Registers a trace processor C++ table function with SQLite.
- void RegisterTableFunction(std::unique_ptr<TableFunction> fn);
+ void RegisterStaticTableFunction(std::unique_ptr<StaticTableFunction> fn);
// Returns the state for the given table function.
- CreatedTableFunction::State* GetTableFunctionState(const std::string&) const;
+ RuntimeTableFunction::State* GetRuntimeTableFunctionState(
+ const std::string&) const;
// Should be called when a table function is destroyed.
- void OnTableFunctionDestroyed(const std::string&);
+ void OnRuntimeTableFunctionDestroyed(const std::string&);
SqliteEngine* sqlite_engine() { return engine_.get(); }
@@ -131,12 +132,12 @@
const PerfettoSqlParser::CreateFunction&);
// Registers a SQL-defined trace processor C++ table with SQLite.
- base::Status RegisterSqlTable(std::string name, SqlSource sql);
+ base::Status RegisterRuntimeTable(std::string name, SqlSource sql);
std::unique_ptr<QueryCache> query_cache_;
StringPool* pool_ = nullptr;
- base::FlatHashMap<std::string, std::unique_ptr<CreatedTableFunction::State>>
- created_table_function_state_;
+ base::FlatHashMap<std::string, std::unique_ptr<RuntimeTableFunction::State>>
+ runtime_table_fn_states_;
std::unique_ptr<SqliteEngine> engine_;
};
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
index d820da6..459c6e0 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
@@ -17,6 +17,7 @@
#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h"
#include <algorithm>
+#include <optional>
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
@@ -62,13 +63,13 @@
} // namespace
PerfettoSqlParser::PerfettoSqlParser(SqlSource sql)
- : sql_(std::move(sql)), tokenizer_(sql_.sql().c_str()) {}
+ : tokenizer_(std::move(sql)) {}
bool PerfettoSqlParser::Next() {
PERFETTO_DCHECK(status_.ok());
State state = State::kStmtStart;
- const char* non_space_ptr = nullptr;
+ std::optional<Token> first_non_space_token;
for (Token token = tokenizer_.Next();; token = tokenizer_.Next()) {
// Space should always be completely ignored by any logic below as it will
// never change the current state in the state machine.
@@ -79,13 +80,9 @@
if (token.IsTerminal()) {
// If we have a non-space character we've seen, just return all the stuff
// after that point.
- if (non_space_ptr) {
- uint32_t offset_of_non_space =
- static_cast<uint32_t>(non_space_ptr - sql_.sql().c_str());
- uint32_t chars_since_non_space =
- static_cast<uint32_t>(tokenizer_.ptr() - non_space_ptr);
+ if (first_non_space_token) {
statement_ =
- SqliteSql{sql_.Substr(offset_of_non_space, chars_since_non_space)};
+ SqliteSql{tokenizer_.Substr(*first_non_space_token, token)};
return true;
}
// This means we've seen a semi-colon without any non-space content. Just
@@ -99,8 +96,8 @@
}
// If we've not seen a space character, keep track of the current position.
- if (!non_space_ptr) {
- non_space_ptr = token.str.data();
+ if (!first_non_space_token) {
+ first_non_space_token = token;
}
switch (state) {
@@ -172,15 +169,9 @@
return ErrorAtToken(token, err.c_str());
}
- Token tok = tokenizer_.NextNonWhitespace();
- Token first = tok;
-
- tok = tokenizer_.NextTerminal();
-
- uint32_t offset = static_cast<uint32_t>(first.str.data() - sql_.sql().data());
- uint32_t len = static_cast<uint32_t>(tok.str.end() - sql_.sql().data());
-
- statement_ = CreateTable{std::move(name), sql_.Substr(offset, len)};
+ Token first = tokenizer_.NextNonWhitespace();
+ statement_ = CreateTable{std::move(name),
+ tokenizer_.Substr(first, tokenizer_.NextTerminal())};
return true;
}
@@ -243,15 +234,9 @@
}
Token first = tokenizer_.NextNonWhitespace();
- Token token = first;
- token = tokenizer_.NextTerminal();
-
- uint32_t offset = static_cast<uint32_t>(first.str.data() - sql_.sql().data());
- uint32_t len = static_cast<uint32_t>((token.str.data() + token.str.size()) -
- first.str.data());
-
- statement_ = CreateFunction{replace, std::move(prototype), std::move(ret),
- sql_.Substr(offset, len), table_return};
+ statement_ = CreateFunction{
+ replace, std::move(prototype), std::move(ret),
+ tokenizer_.Substr(first, tokenizer_.NextTerminal()), table_return};
return true;
}
@@ -282,8 +267,7 @@
bool PerfettoSqlParser::ErrorAtToken(const SqliteTokenizer::Token& token,
const char* error) {
- uint32_t offset = static_cast<uint32_t>(token.str.data() - sql_.sql().data());
- std::string traceback = sql_.AsTraceback(offset);
+ std::string traceback = tokenizer_.AsTraceback(token);
status_ = base::ErrStatus("%s%s", traceback.c_str(), error);
return false;
}
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h
index 6631d38..52bf68f 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h
@@ -97,7 +97,6 @@
bool ErrorAtToken(const SqliteTokenizer::Token&, const char* error);
- SqlSource sql_;
SqliteTokenizer tokenizer_;
base::Status status_;
std::optional<Statement> statement_;
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc
index 555f25a..e5eb2f6 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc
@@ -81,19 +81,24 @@
TEST_F(PerfettoSqlParserTest, SemiColonTerminatedStatement) {
auto res = SqlSource::FromExecuteQuery("SELECT * FROM slice;");
- ASSERT_THAT(*Parse(res), testing::ElementsAre(SqliteSql{res}));
+ ASSERT_THAT(
+ *Parse(res),
+ testing::ElementsAre(SqliteSql{FindSubstr(res, "SELECT * FROM slice")}));
}
TEST_F(PerfettoSqlParserTest, MultipleStmts) {
auto res =
SqlSource::FromExecuteQuery("SELECT * FROM slice; SELECT * FROM s");
- ASSERT_THAT(*Parse(res), testing::ElementsAre(SqliteSql{res.Substr(0, 20)},
- SqliteSql{res.Substr(21, 15)}));
+ ASSERT_THAT(
+ *Parse(res),
+ testing::ElementsAre(SqliteSql{FindSubstr(res, "SELECT * FROM slice")},
+ SqliteSql{FindSubstr(res, "SELECT * FROM s")}));
}
TEST_F(PerfettoSqlParserTest, IgnoreOnlySpace) {
auto res = SqlSource::FromExecuteQuery(" ; SELECT * FROM s; ; ;");
- ASSERT_THAT(*Parse(res), testing::ElementsAre(SqliteSql{res.Substr(3, 16)}));
+ ASSERT_THAT(*Parse(res), testing::ElementsAre(
+ SqliteSql{FindSubstr(res, "SELECT * FROM s")}));
}
TEST_F(PerfettoSqlParserTest, CreatePerfettoFunctionScalar) {
@@ -135,10 +140,10 @@
TEST_F(PerfettoSqlParserTest, CreatePerfettoFunctionAndOther) {
auto res = SqlSource::FromExecuteQuery(
"create perfetto function foo() returns INT as select 1; select foo()");
- ASSERT_THAT(*Parse(res), testing::ElementsAre(
- CreateFn{false, "foo()", "INT",
- FindSubstr(res, "select 1;"), false},
- SqliteSql{FindSubstr(res, "select foo()")}));
+ ASSERT_THAT(*Parse(res),
+ testing::ElementsAre(CreateFn{false, "foo()", "INT",
+ FindSubstr(res, "select 1"), false},
+ SqliteSql{FindSubstr(res, "select foo()")}));
}
} // namespace
diff --git a/src/trace_processor/perfetto_sql/engine/created_table_function.cc b/src/trace_processor/perfetto_sql/engine/runtime_table_function.cc
similarity index 90%
rename from src/trace_processor/perfetto_sql/engine/created_table_function.cc
rename to src/trace_processor/perfetto_sql/engine/runtime_table_function.cc
index 22ef34a..7804e7a 100644
--- a/src/trace_processor/perfetto_sql/engine/created_table_function.cc
+++ b/src/trace_processor/perfetto_sql/engine/runtime_table_function.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "src/trace_processor/perfetto_sql/engine/created_table_function.h"
+#include "src/trace_processor/perfetto_sql/engine/runtime_table_function.h"
#include <optional>
#include <utility>
@@ -34,24 +34,24 @@
} // namespace
-CreatedTableFunction::CreatedTableFunction(sqlite3*, PerfettoSqlEngine* engine)
+RuntimeTableFunction::RuntimeTableFunction(sqlite3*, PerfettoSqlEngine* engine)
: engine_(engine) {}
-CreatedTableFunction::~CreatedTableFunction() {
- engine_->OnTableFunctionDestroyed(name());
+RuntimeTableFunction::~RuntimeTableFunction() {
+ engine_->OnRuntimeTableFunctionDestroyed(name());
}
-base::Status CreatedTableFunction::Init(int,
+base::Status RuntimeTableFunction::Init(int,
const char* const*,
Schema* schema) {
- state_ = engine_->GetTableFunctionState(name());
+ state_ = engine_->GetRuntimeTableFunctionState(name());
// Now we've parsed prototype and return values, create the schema.
*schema = CreateSchema();
return base::OkStatus();
}
-SqliteTable::Schema CreatedTableFunction::CreateSchema() {
+SqliteTable::Schema RuntimeTableFunction::CreateSchema() {
std::vector<Column> columns;
for (size_t i = 0; i < state_->return_values.size(); ++i) {
const auto& ret = state_->return_values[i];
@@ -82,11 +82,11 @@
return SqliteTable::Schema(std::move(columns), std::move(primary_keys));
}
-std::unique_ptr<SqliteTable::BaseCursor> CreatedTableFunction::CreateCursor() {
+std::unique_ptr<SqliteTable::BaseCursor> RuntimeTableFunction::CreateCursor() {
return std::unique_ptr<Cursor>(new Cursor(this, state_));
}
-int CreatedTableFunction::BestIndex(const QueryConstraints& qc,
+int RuntimeTableFunction::BestIndex(const QueryConstraints& qc,
BestIndexInfo* info) {
// Only accept constraint sets where every input parameter has a value.
size_t seen_argument_constraints = 0;
@@ -107,7 +107,7 @@
return SQLITE_OK;
}
-CreatedTableFunction::Cursor::Cursor(CreatedTableFunction* table, State* state)
+RuntimeTableFunction::Cursor::Cursor(RuntimeTableFunction* table, State* state)
: SqliteTable::BaseCursor(table), table_(table), state_(state) {
if (state->reusable_stmt) {
stmt_ = std::move(state->reusable_stmt);
@@ -116,14 +116,14 @@
}
}
-CreatedTableFunction::Cursor::~Cursor() {
+RuntimeTableFunction::Cursor::~Cursor() {
if (return_stmt_to_state_) {
ResetStatement(stmt_->sqlite_stmt());
state_->reusable_stmt = std::move(stmt_);
}
}
-base::Status CreatedTableFunction::Cursor::Filter(const QueryConstraints& qc,
+base::Status RuntimeTableFunction::Cursor::Filter(const QueryConstraints& qc,
sqlite3_value** argv,
FilterHistory) {
PERFETTO_TP_TRACE(metatrace::Category::FUNCTION, "TABLE_FUNCTION_CALL",
@@ -216,17 +216,17 @@
return Next();
}
-base::Status CreatedTableFunction::Cursor::Next() {
+base::Status RuntimeTableFunction::Cursor::Next() {
is_eof_ = !stmt_->Step();
next_call_count_++;
return stmt_->status();
}
-bool CreatedTableFunction::Cursor::Eof() {
+bool RuntimeTableFunction::Cursor::Eof() {
return is_eof_;
}
-base::Status CreatedTableFunction::Cursor::Column(sqlite3_context* ctx, int i) {
+base::Status RuntimeTableFunction::Cursor::Column(sqlite3_context* ctx, int i) {
size_t idx = static_cast<size_t>(i);
if (state_->IsReturnValueColumn(idx)) {
sqlite3_result_value(ctx, sqlite3_column_value(stmt_->sqlite_stmt(), i));
diff --git a/src/trace_processor/perfetto_sql/engine/created_table_function.h b/src/trace_processor/perfetto_sql/engine/runtime_table_function.h
similarity index 82%
rename from src/trace_processor/perfetto_sql/engine/created_table_function.h
rename to src/trace_processor/perfetto_sql/engine/runtime_table_function.h
index 8ae3ca2..9fe8c6e 100644
--- a/src/trace_processor/perfetto_sql/engine/created_table_function.h
+++ b/src/trace_processor/perfetto_sql/engine/runtime_table_function.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_ENGINE_CREATED_TABLE_FUNCTION_H_
-#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_ENGINE_CREATED_TABLE_FUNCTION_H_
+#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_ENGINE_RUNTIME_TABLE_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_ENGINE_RUNTIME_TABLE_FUNCTION_H_
#include <optional>
@@ -29,11 +29,11 @@
// The implementation of the SqliteTable interface for table functions defined
// at runtime using SQL.
-class CreatedTableFunction final
- : public TypedSqliteTable<CreatedTableFunction, PerfettoSqlEngine*> {
+class RuntimeTableFunction final
+ : public TypedSqliteTable<RuntimeTableFunction, PerfettoSqlEngine*> {
public:
- // The state of this function. This is separated from |CreatedTableFunction|
- // because |CreatedTableFunction| is owned by Sqlite while |State| is owned by
+ // The state of this function. This is separated from |RuntimeTableFunction|
+ // because |RuntimeTableFunction| is owned by Sqlite while |State| is owned by
// PerfettoSqlEngine.
struct State {
std::string prototype_str;
@@ -68,7 +68,7 @@
};
class Cursor final : public SqliteTable::BaseCursor {
public:
- explicit Cursor(CreatedTableFunction* table, State* state);
+ explicit Cursor(RuntimeTableFunction* table, State* state);
~Cursor() final;
base::Status Filter(const QueryConstraints& qc,
@@ -79,7 +79,7 @@
base::Status Column(sqlite3_context* context, int N);
private:
- CreatedTableFunction* table_ = nullptr;
+ RuntimeTableFunction* table_ = nullptr;
State* state_ = nullptr;
std::optional<SqliteEngine::PreparedStatement> stmt_;
@@ -89,8 +89,8 @@
int next_call_count_ = 0;
};
- CreatedTableFunction(sqlite3*, PerfettoSqlEngine*);
- ~CreatedTableFunction() final;
+ RuntimeTableFunction(sqlite3*, PerfettoSqlEngine*);
+ ~RuntimeTableFunction() final;
base::Status Init(int argc, const char* const* argv, Schema*) final;
std::unique_ptr<SqliteTable::BaseCursor> CreateCursor() final;
@@ -106,4 +106,4 @@
} // namespace trace_processor
} // namespace perfetto
-#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_ENGINE_CREATED_TABLE_FUNCTION_H_
+#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_ENGINE_RUNTIME_TABLE_FUNCTION_H_
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
index 46e6e68..1375d9e 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
@@ -62,8 +62,8 @@
source_set("interface") {
sources = [
- "table_function.cc",
- "table_function.h",
+ "static_table_function.cc",
+ "static_table_function.h",
]
deps = [
"../../../../../gn:default_deps",
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.h
index a2dfce2..4aa8bd2 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/ancestor.h
@@ -19,7 +19,7 @@
#include <optional>
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
@@ -33,7 +33,7 @@
// * ancestor_slice_by_stack
//
// See docs/analysis/trace-processor for usage.
-class Ancestor : public TableFunction {
+class Ancestor : public StaticTableFunction {
public:
enum class Type { kSlice = 1, kStackProfileCallsite = 2, kSliceByStack = 3 };
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/connected_flow.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/connected_flow.h
index 9206968..6a7a804 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/connected_flow.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/connected_flow.h
@@ -17,7 +17,7 @@
#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_CONNECTED_FLOW_H_
#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_CONNECTED_FLOW_H_
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/tables_py.h"
#include "src/trace_processor/storage/trace_storage.h"
@@ -33,7 +33,7 @@
// - DIRECTLY_CONNECTED_FLOW
// - PRECEDING_FLOW
// - FOLLOWING_FLOW
-class ConnectedFlow : public TableFunction {
+class ConnectedFlow : public StaticTableFunction {
public:
enum class Mode {
// Directly connected slices through the same flow ID given by the trace
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.h
index aa0c220..2f7a870 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.h
@@ -19,7 +19,7 @@
#include <optional>
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
@@ -32,7 +32,7 @@
// * descendant_slice_by_stack
//
// See docs/analysis/trace-processor for usage.
-class Descendant : public TableFunction {
+class Descendant : public StaticTableFunction {
public:
enum class Type { kSlice = 1, kSliceByStack = 2 };
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_annotated_stack.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_annotated_stack.h
index 49878a3..193386b 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_annotated_stack.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_annotated_stack.h
@@ -17,7 +17,7 @@
#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_EXPERIMENTAL_ANNOTATED_STACK_H_
#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_EXPERIMENTAL_ANNOTATED_STACK_H_
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
namespace perfetto {
namespace trace_processor {
@@ -29,7 +29,7 @@
// Given a leaf callsite id, returns the full callstack (including the leaf),
// with optional (currently Android-specific) annotations. A given callsite will
// always have the same annotation.
-class ExperimentalAnnotatedStack : public TableFunction {
+class ExperimentalAnnotatedStack : public StaticTableFunction {
public:
explicit ExperimentalAnnotatedStack(TraceProcessorContext* context)
: context_(context) {}
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_counter_dur.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_counter_dur.h
index 038bcb0..045c8e2 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_counter_dur.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_counter_dur.h
@@ -17,13 +17,13 @@
#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_EXPERIMENTAL_COUNTER_DUR_H_
#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_EXPERIMENTAL_COUNTER_DUR_H_
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
-class ExperimentalCounterDur : public TableFunction {
+class ExperimentalCounterDur : public StaticTableFunction {
public:
using CounterTable = tables::CounterTable;
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.h
index 8a4cae5..6bc172f 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flamegraph.h
@@ -18,7 +18,7 @@
#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_EXPERIMENTAL_FLAMEGRAPH_H_
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.h"
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
@@ -26,7 +26,7 @@
class TraceProcessorContext;
-class ExperimentalFlamegraph : public TableFunction {
+class ExperimentalFlamegraph : public StaticTableFunction {
public:
enum class ProfileType { kGraph, kHeapProfile, kPerf };
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flat_slice.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flat_slice.h
index 218923e..e9423ed 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flat_slice.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flat_slice.h
@@ -19,7 +19,7 @@
#include <optional>
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
@@ -53,7 +53,7 @@
// (which picks all slices with ts + dur >= bound) and is more akin to doing
// a simple ts >= bound. However, slices *will* be truncated at the end
// if they would spill past the provided end bound.
-class ExperimentalFlatSlice : public TableFunction {
+class ExperimentalFlatSlice : public StaticTableFunction {
public:
ExperimentalFlatSlice(TraceProcessorContext* context);
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h
index 800e933..41ff35c 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h
@@ -19,13 +19,13 @@
#include <set>
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
-class ExperimentalSchedUpid : public TableFunction {
+class ExperimentalSchedUpid : public StaticTableFunction {
public:
ExperimentalSchedUpid(const tables::SchedSliceTable&,
const tables::ThreadTable&);
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h
index d23c628..9fcd299 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h
@@ -19,13 +19,13 @@
#include <set>
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
-class ExperimentalSliceLayout : public TableFunction {
+class ExperimentalSliceLayout : public StaticTableFunction {
public:
ExperimentalSliceLayout(StringPool* string_pool,
const tables::SliceTable* table);
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.cc
similarity index 90%
rename from src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.cc
rename to src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.cc
index 770f51f..0799e13 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.cc
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.cc
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
namespace perfetto {
namespace trace_processor {
-TableFunction::~TableFunction() = default;
+StaticTableFunction::~StaticTableFunction() = default;
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h
similarity index 94%
rename from src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h
rename to src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h
index 4079603..75b6c79 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_TABLE_FUNCTION_H_
-#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_TABLE_FUNCTION_H_
+#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_STATIC_TABLE_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_STATIC_TABLE_FUNCTION_H_
#include "perfetto/base/status.h"
#include "src/trace_processor/db/table.h"
@@ -28,9 +28,9 @@
// at filter time.
// This class is used to implement table-valued functions and other similar
// tables.
-class TableFunction {
+class StaticTableFunction {
public:
- virtual ~TableFunction();
+ virtual ~StaticTableFunction();
// Returns the schema of the table that will be returned by ComputeTable.
virtual Table::Schema CreateSchema() = 0;
@@ -62,4 +62,4 @@
} // namespace trace_processor
} // namespace perfetto
-#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_TABLE_FUNCTION_H_
+#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_STATIC_TABLE_FUNCTION_H_
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.cc
index fca72a4..1055357 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.cc
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.cc
@@ -19,32 +19,35 @@
namespace perfetto {
namespace trace_processor {
-ViewTableFunction::ViewTableFunction(const View* view, const char* name)
+ViewStaticTableFunction::ViewStaticTableFunction(const View* view,
+ const char* name)
: view_(view), name_(name) {}
-ViewTableFunction::~ViewTableFunction() = default;
+ViewStaticTableFunction::~ViewStaticTableFunction() = default;
-base::Status ViewTableFunction::ValidateConstraints(const QueryConstraints&) {
+base::Status ViewStaticTableFunction::ValidateConstraints(
+ const QueryConstraints&) {
return base::OkStatus();
}
-base::Status ViewTableFunction::ComputeTable(const std::vector<Constraint>& cs,
- const std::vector<Order>& ob,
- const BitVector& cols_used,
- std::unique_ptr<Table>& table) {
+base::Status ViewStaticTableFunction::ComputeTable(
+ const std::vector<Constraint>& cs,
+ const std::vector<Order>& ob,
+ const BitVector& cols_used,
+ std::unique_ptr<Table>& table) {
table.reset(new Table(view_->Query(cs, ob, cols_used)));
return base::OkStatus();
}
-Table::Schema ViewTableFunction::CreateSchema() {
+Table::Schema ViewStaticTableFunction::CreateSchema() {
return view_->schema();
}
-std::string ViewTableFunction::TableName() {
+std::string ViewStaticTableFunction::TableName() {
return name_;
}
-uint32_t ViewTableFunction::EstimateRowCount() {
+uint32_t ViewStaticTableFunction::EstimateRowCount() {
return view_->EstimateRowCount();
}
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.h
index 79b3a5c..0bb4648 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/view.h
@@ -19,16 +19,16 @@
#include "perfetto/ext/base/flat_hash_map.h"
#include "src/trace_processor/db/view.h"
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
-class ViewTableFunction : public TableFunction {
+class ViewStaticTableFunction : public StaticTableFunction {
public:
- explicit ViewTableFunction(const View*, const char* name);
- ~ViewTableFunction() override;
+ explicit ViewStaticTableFunction(const View*, const char* name);
+ ~ViewStaticTableFunction() override;
Table::Schema CreateSchema() override;
std::string TableName() override;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/binder.sql b/src/trace_processor/perfetto_sql/stdlib/android/binder.sql
index 0b95ee1..f0a1a66 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/binder.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/binder.sql
@@ -418,3 +418,82 @@
SELECT *, 1 AS is_sync FROM android_sync_binder_metrics_by_txn
UNION ALL
SELECT *, 0 AS is_sync FROM android_async_binder_metrics_by_txn;
+
+-- Returns a DAG of all outgoing binder txns from a process.
+-- The roots of the graph are the threads making the txns and the graph flows from:
+-- thread -> server_process -> AIDL interface -> AIDL method.
+-- The weights of each node represent the wall execution time in the server_process.
+--
+-- @arg upid STRING Upid of process to generate an outgoing graph for.
+-- @ret pprof BYTES Pprof of outgoing binder txns.
+CREATE PERFETTO FUNCTION ANDROID_BINDER_OUTGOING_GRAPH(upid INT)
+RETURNS TABLE(pprof BYTES) AS
+WITH threads AS (
+ SELECT binder_txn_id, CAT_STACKS(client_thread) AS stack
+ FROM android_binder_txns
+ WHERE ($upid IS NOT NULL AND client_upid = $upid) OR ($upid IS NULL)
+), server_process AS (
+ SELECT binder_txn_id, CAT_STACKS(stack, server_process) AS stack
+ FROM android_binder_txns
+ JOIN threads USING(binder_txn_id)
+), end_points AS (
+ SELECT binder_txn_id,
+ CAT_STACKS(stack, STR_SPLIT(aidl_name, '::', IIF(aidl_name GLOB 'AIDL*', 2, 1))) AS stack
+ FROM android_binder_txns
+ JOIN server_process USING(binder_txn_id)
+), aidl_names AS (
+ SELECT binder_txn_id, server_dur,
+ CAT_STACKS(stack, STR_SPLIT(aidl_name, '::', IIF(aidl_name GLOB 'AIDL*', 3, 2))) AS stack
+ FROM android_binder_txns
+ JOIN end_points USING(binder_txn_id)
+) SELECT EXPERIMENTAL_PROFILE(stack, 'duration', 'ns', server_dur) AS pprof
+ FROM aidl_names;
+
+-- Returns a DAG of all incoming binder txns from a process.
+-- The roots of the graph are the clients making the txns and the graph flows from:
+-- client_process -> AIDL interface -> AIDL method.
+-- The weights of each node represent the wall execution time in the server_process.
+--
+-- @arg upid STRING Upid of process to generate an outgoing graph for.
+-- @ret pprof BYTES Pprof of outgoing binder txns.
+CREATE PERFETTO FUNCTION ANDROID_BINDER_INCOMING_GRAPH(upid INT)
+RETURNS TABLE(pprof BYTES) AS
+WITH client_process AS (
+ SELECT binder_txn_id, CAT_STACKS(client_process) AS stack
+ FROM android_binder_txns
+ WHERE ($upid IS NOT NULL AND server_upid = $upid) OR ($upid IS NULL)
+), end_points AS (
+ SELECT binder_txn_id,
+ CAT_STACKS(stack, STR_SPLIT(aidl_name, '::', IIF(aidl_name GLOB 'AIDL*', 2, 1))) AS stack
+ FROM android_binder_txns
+ JOIN client_process USING(binder_txn_id)
+), aidl_names AS (
+ SELECT binder_txn_id, server_dur,
+ CAT_STACKS(stack, STR_SPLIT(aidl_name, '::', IIF(aidl_name GLOB 'AIDL*', 3, 2))) AS stack
+ FROM android_binder_txns
+ JOIN end_points USING(binder_txn_id)
+) SELECT EXPERIMENTAL_PROFILE(stack, 'duration', 'ns', server_dur) AS pprof
+ FROM aidl_names;
+
+-- Returns a graph of all binder txns in a trace.
+-- The nodes are client_process and server_process.
+-- The weights of each node represent the wall execution time in the server_process.
+--
+-- @arg min_client_oom_score INT Matches txns from client_processes greater than or equal to the OOM score.
+-- @arg max_client_oom_score INT Matches txns from client_processes less than or equal to the OOM score.
+-- @arg min_server_oom_score INT Matches txns to server_processes greater than or equal to the OOM score.
+-- @arg max_server_oom_score INT Matches txns to server_processes less than or equal to the OOM score.
+-- @ret pprof BYTES Pprof of binder txns.
+CREATE PERFETTO FUNCTION ANDROID_BINDER_GRAPH(min_client_oom_score INT, max_client_oom_score INT, min_server_oom_score INT, max_server_oom_score INT)
+RETURNS TABLE(pprof BYTES) AS
+WITH clients AS (
+ SELECT binder_txn_id, CAT_STACKS(client_process) AS stack
+ FROM android_binder_txns
+ WHERE client_oom_score BETWEEN $min_client_oom_score AND $max_client_oom_score
+), servers AS (
+ SELECT binder_txn_id, server_dur, CAT_STACKS(stack, server_process) AS stack
+ FROM android_binder_txns
+ JOIN clients USING(binder_txn_id)
+ WHERE server_oom_score BETWEEN $min_server_oom_score AND $max_server_oom_score
+) SELECT EXPERIMENTAL_PROFILE(stack, 'duration', 'ns', server_dur) AS pprof
+ FROM servers;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql b/src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql
index 5baf1b3..10727b5 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql
@@ -370,3 +370,40 @@
FROM android_monitor_contention_chain_thread_state
WHERE blocked_function IS NOT NULL
GROUP BY id, blocked_function;
+
+-- Returns a DAG of all Java lock contentions in a process.
+-- Each node in the graph is a <thread:Java method> pair.
+-- Each edge connects from a node waiting on a lock to a node holding a lock.
+-- The weights of each node represent the cumulative wall time the node blocked
+-- other nodes connected to it.
+--
+-- @arg upid INT Upid of process to generate a lock graph for.
+-- @ret pprof BYTES Pprof of lock graph.
+CREATE PERFETTO FUNCTION android_monitor_contention_graph(upid INT)
+RETURNS TABLE(pprof BYTES) AS
+WITH contention_chain AS (
+SELECT *,
+ IIF(blocked_thread_name LIKE 'binder:%', 'binder', blocked_thread_name)
+ AS blocked_thread_name_norm,
+ IIF(blocking_thread_name LIKE 'binder:%', 'binder', blocking_thread_name)
+ AS blocking_thread_name_norm
+FROM android_monitor_contention_chain WHERE upid = $upid
+GROUP BY id, parent_id
+), graph AS (
+SELECT
+ id,
+ dur,
+ CAT_STACKS(blocked_thread_name_norm || ':' || short_blocked_method,
+ blocking_thread_name_norm || ':' || short_blocking_method) AS stack
+FROM contention_chain
+WHERE parent_id IS NULL
+UNION ALL
+SELECT
+c.id,
+c.dur AS dur,
+ CAT_STACKS(blocked_thread_name_norm || ':' || short_blocked_method,
+ blocking_thread_name_norm || ':' || short_blocking_method, stack) AS stack
+FROM contention_chain c, graph AS p
+WHERE p.id = c.parent_id
+) SELECT EXPERIMENTAL_PROFILE(stack, 'duration', 'ns', dur) AS pprof
+ FROM graph;
diff --git a/src/trace_processor/perfetto_sql/stdlib/experimental/thread_executing_span.sql b/src/trace_processor/perfetto_sql/stdlib/experimental/thread_executing_span.sql
index 7bb1344..93cb24d 100644
--- a/src/trace_processor/perfetto_sql/stdlib/experimental/thread_executing_span.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/experimental/thread_executing_span.sql
@@ -492,7 +492,7 @@
CREATE PERFETTO FUNCTION
experimental_thread_executing_span_id_from_thread_state_id(thread_state_id INT)
RETURNS INT AS
-WITH t AS (
+WITH executing AS (
SELECT
ts,
utid
@@ -502,5 +502,140 @@
)
SELECT
MAX(start_id) AS thread_executing_span_id
-FROM internal_wakeup w, t
-WHERE t.utid = w.utid AND t.ts >= w.start_ts AND t.ts < w.end_ts;
+FROM internal_wakeup wakeup, executing
+WHERE executing.utid = wakeup.utid AND executing.ts >= wakeup.start_ts AND executing.ts < wakeup.end_ts;
+
+-- Gets the next thread_executing_span id after a sleeping state. Returns NULL if there is no
+-- thread_executing_span after the |thread_state_id|.
+--
+-- @arg thread_state_id INT Id of the thread_state to get the next thread_executing_span id for
+-- @ret INT thread_executing_span id
+CREATE PERFETTO FUNCTION
+experimental_thread_executing_span_following_thread_state_id(thread_state_id INT)
+RETURNS INT AS
+WITH
+ sleeping AS (
+ SELECT
+ ts,
+ utid
+ FROM thread_state
+ WHERE
+ id = $thread_state_id AND (state = 'S' OR state = 'D' OR state = 'I')
+ )
+SELECT MIN(start_id) AS thread_executing_span_id
+FROM internal_wakeup wakeup, sleeping
+WHERE sleeping.utid = wakeup.utid AND sleeping.ts < wakeup.start_ts;
+
+-- Computes the start and end of each thread_executing_span in the critical path.
+
+-- For the ends, it is the MIN between the end of the current span and the start of
+-- the next span in the critical path.
+
+-- For the starts, it is the MAX between the start of the critical span and the start
+-- of the blocked region. This ensures that the critical path doesn't overlap regions
+-- that are not actually blocked.
+CREATE PERFETTO FUNCTION internal_compute_critical_path_boundaries(thread_executing_span_id INT)
+RETURNS TABLE(id INT, ts LONG, dur LONG) AS
+SELECT
+ id,
+ MAX(ts, leaf_ts - leaf_blocked_dur) AS ts,
+ MIN(
+ MAX(ts, leaf_ts - leaf_blocked_dur) + dur,
+ IFNULL(LEAD(ts) OVER (PARTITION BY leaf_id ORDER BY height DESC), trace_bounds.end_ts))
+ - MAX(ts, leaf_ts - leaf_blocked_dur) AS dur
+FROM EXPERIMENTAL_THREAD_EXECUTING_SPAN_ANCESTORS($thread_executing_span_id), trace_bounds;
+
+-- Critical path of thread_executing_spans blocking the thread_executing_span with id,
+-- |thread_executing_span_id|. For a given thread state span, its duration in the critical path
+-- is the range between the start of the thread_executing_span and the start of the next span in the
+-- critical path.
+--
+-- @arg thread_executing_span_id INT Id of blocked thread_executing_span.
+--
+-- @column parent_id Id of thread_executing_span that directly woke |id|.
+-- @column id Id of the first (runnable) thread state in thread_executing_span.
+-- @column ts Timestamp of first thread_state in thread_executing_span.
+-- @column dur Duration of thread_executing_span within the critical path.
+-- @column tid Tid of thread with thread_state.
+-- @column pid Pid of process with thread_state.
+-- @column utid Utid of thread with thread_state.
+-- @column upid Upid of process with thread_state.
+-- @column thread_name Name of thread with thread_state.
+-- @column process_name Name of process with thread_state.
+-- @column waker_tid Tid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_pid Pid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_utid Utid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_upid Upid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_thread_name Name of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_process_name Name of process that woke the first thread_state in thread_executing_span.
+-- @column blocked_dur Duration of blocking thread state before waking up.
+-- @column blocked_state Thread state ('D' or 'S') of blocked thread_state before waking up.
+-- @column blocked_function Kernel blocking function of thread state before waking up.
+-- @column is_root Whether this span is the root in the slice tree.
+-- @column is_leaf Whether this span is the leaf in the slice tree.
+-- @column height Tree height from |leaf_id|.
+-- @column leaf_id Thread state id used to start the recursion. Helpful for SQL JOINs.
+-- @column leaf_ts Thread state timestamp of the |leaf_id|.
+-- @column leaf_blocked_dur Thread state duration blocked of the |leaf_id|.
+-- @column leaf_blocked_state Thread state of the |leaf_id|.
+-- @column leaf_blocked_function Thread state blocked_function of the |leaf_id|.
+CREATE PERFETTO FUNCTION experimental_thread_executing_span_critical_path(thread_executing_span_id INT)
+RETURNS TABLE(
+ parent_id LONG,
+ id LONG,
+ ts LONG,
+ dur LONG,
+ tid INT,
+ pid INT,
+ utid INT,
+ upid INT,
+ thread_name STRING,
+ process_name STRING,
+ waker_tid INT,
+ waker_pid INT,
+ waker_utid INT,
+ waker_upid INT,
+ waker_thread_name STRING,
+ waker_process_name STRING,
+ blocked_dur LONG,
+ blocked_state STRING,
+ blocked_function STRING,
+ is_root INT,
+ is_leaf INT,
+ height INT,
+ leaf_id INT,
+ leaf_ts LONG,
+ leaf_blocked_dur LONG,
+ leaf_blocked_state STRING,
+ leaf_blocked_function STRING
+) AS
+ SELECT
+ parent_id,
+ id,
+ boundary.ts,
+ boundary.dur,
+ tid,
+ pid,
+ utid,
+ upid,
+ thread_name,
+ process_name,
+ waker_tid,
+ waker_pid,
+ waker_utid,
+ waker_upid,
+ waker_thread_name,
+ waker_process_name,
+ blocked_dur,
+ blocked_state,
+ blocked_function,
+ is_root,
+ is_leaf,
+ height,
+ leaf_id,
+ leaf_ts,
+ leaf_blocked_dur,
+ leaf_blocked_state,
+ leaf_blocked_function
+ FROM experimental_thread_executing_span_ancestors($thread_executing_span_id)
+ JOIN internal_compute_critical_path_boundaries($thread_executing_span_id) boundary USING(id);
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index e9c793d..940a65d 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -21,7 +21,7 @@
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/db/runtime_table.h"
#include "src/trace_processor/db/table.h"
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/sqlite/query_cache.h"
#include "src/trace_processor/sqlite/sqlite_table.h"
@@ -29,14 +29,13 @@
namespace trace_processor {
enum class DbSqliteTableComputation {
- // Mode when the table is static (i.e. passed in at construction
- // time).
+ // Table is statically defined.
kStatic,
- // Mode when table is dynamically computed at filter time.
+ // Table is defined as a function.
kTableFunction,
- // Mode when table is dynamically computer at SQL runtime.
+ // Table is defined in runtime.
kRuntime
};
@@ -47,11 +46,11 @@
// Only valid when computation == TableComputation::kStatic.
const Table* static_table;
- // Only valid when computation == TableComputation::kSql.
+ // Only valid when computation == TableComputation::kRuntime.
std::unique_ptr<RuntimeTable> sql_table;
- // Only valid when computation == TableComputation::kDynamic.
- std::unique_ptr<TableFunction> generator;
+ // Only valid when computation == TableComputation::kTableFunction.
+ std::unique_ptr<StaticTableFunction> generator;
};
// Implements the SQLite table interface for db tables.
@@ -171,8 +170,8 @@
// Only valid when computation_ == TableComputation::kSql.
std::unique_ptr<RuntimeTable> sql_table_;
- // Only valid when computation_ == TableComputation::kDynamic.
- std::unique_ptr<TableFunction> generator_;
+ // Only valid when computation_ == TableComputation::kTableFunction.
+ std::unique_ptr<StaticTableFunction> generator_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index b530870..245fe7c 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -231,13 +231,12 @@
// This name of the table. For tables created using CREATE VIRTUAL TABLE, this
// will be the name of the table specified by the query. For automatically
- // created tables, this will be the same as the module name passed to
- // RegisterTable.
+ // created tables, this will be the same as the module name registered.
std::string name_;
- // The module name is the name passed to RegisterTable. This is differs from
- // the table name (|name_|) where the table was created using CREATE VIRTUAL
- // TABLE.
+ // The module name is the name that will be registered. This is
+ // differs from the table name (|name_|) where the table was created using
+ // CREATE VIRTUAL TABLE.
std::string module_name_;
Schema schema_;
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer.cc b/src/trace_processor/sqlite/sqlite_tokenizer.cc
index c582637..6d07c53 100644
--- a/src/trace_processor/sqlite/sqlite_tokenizer.cc
+++ b/src/trace_processor/sqlite/sqlite_tokenizer.cc
@@ -426,13 +426,14 @@
} // namespace
-SqliteTokenizer::SqliteTokenizer(const char* sql) : ptr_(sql) {}
+SqliteTokenizer::SqliteTokenizer(SqlSource sql) : source_(std::move(sql)) {}
SqliteTokenizer::Token SqliteTokenizer::Next() {
Token token;
- const char* start = ptr_;
- int n = GetSqliteToken(unsigned_ptr(), &token.token_type);
- ptr_ += n;
+ const char* start = source_.sql().data() + offset_;
+ int n = GetSqliteToken(reinterpret_cast<const unsigned char*>(start),
+ &token.token_type);
+ offset_ += static_cast<uint32_t>(n);
token.str = std::string_view(start, static_cast<uint32_t>(n));
return token;
}
@@ -452,5 +453,18 @@
return tok;
}
+SqlSource SqliteTokenizer::Substr(Token start, Token end) const {
+ uint32_t offset =
+ static_cast<uint32_t>(start.str.data() - source_.sql().c_str());
+ uint32_t len = static_cast<uint32_t>(end.str.data() - start.str.data());
+ return source_.Substr(offset, len);
+}
+
+std::string SqliteTokenizer::AsTraceback(Token token) const {
+ uint32_t offset =
+ static_cast<uint32_t>(token.str.data() - source_.sql().c_str());
+ return source_.AsTraceback(offset);
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer.h b/src/trace_processor/sqlite/sqlite_tokenizer.h
index 507d46e..c2b4ccc 100644
--- a/src/trace_processor/sqlite/sqlite_tokenizer.h
+++ b/src/trace_processor/sqlite/sqlite_tokenizer.h
@@ -19,6 +19,7 @@
#include <optional>
#include <string_view>
+#include "src/trace_processor/sqlite/sql_source.h"
namespace perfetto {
namespace trace_processor {
@@ -67,7 +68,7 @@
// https://www2.sqlite.org/hlr40000.html
//
// Usage of this class:
-// SqliteTokenizer tzr(my_sql_string.c_str());
+// SqliteTokenizer tzr(std::move(my_sql_source));
// for (auto t = tzr.Next(); t.token_type != TK_SEMI; t = tzr.Next()) {
// // Handle t here
// }
@@ -91,7 +92,7 @@
}
};
- explicit SqliteTokenizer(const char* sql);
+ explicit SqliteTokenizer(SqlSource sql);
// Returns the next SQL token.
Token Next();
@@ -102,15 +103,32 @@
// Returns the next SQL token which is terminal.
Token NextTerminal();
- // Returns the pointer to the start of the next token which will be returned.
- const char* ptr() const { return ptr_; }
+ // Returns an SqlSource containing all the tokens between |start| and |end|.
+ //
+ // Note: |start| and |end| must both have been previously returned by this
+ // tokenizer.
+ SqlSource Substr(Token start, Token end) const;
- private:
- const unsigned char* unsigned_ptr() const {
- return reinterpret_cast<const unsigned char*>(ptr_);
+ // Returns a traceback error message for the SqlSource backing this tokenizer
+ // pointing to |token|. See SqlSource::AsTraceback for more information about
+ // this method.
+ //
+ // Note: |token| must have been previously returned by this tokenizer.
+ std::string AsTraceback(Token) const;
+
+ // Resets this tokenizer to tokenize |source|. Any previous returned tokens
+ // are invalidated.
+ void Reset(SqlSource source) {
+ source_ = std::move(source);
+ offset_ = 0;
}
- const char* ptr_ = nullptr;
+ private:
+ SqliteTokenizer(SqliteTokenizer&&) = delete;
+ SqliteTokenizer& operator=(SqliteTokenizer&&) = delete;
+
+ SqlSource source_;
+ uint32_t offset_ = 0;
};
} // namespace trace_processor
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc b/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc
index 44946b7..e2301fa 100644
--- a/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc
+++ b/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc
@@ -18,6 +18,7 @@
#include <vector>
#include "perfetto/base/logging.h"
+#include "src/trace_processor/sqlite/sql_source.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
@@ -30,13 +31,16 @@
class SqliteTokenizerTest : public ::testing::Test {
protected:
std::vector<SqliteTokenizer::Token> Tokenize(const char* ptr) {
- SqliteTokenizer tokenizer(ptr);
+ tokenizer_.Reset(SqlSource::FromTraceProcessorImplementation(ptr));
std::vector<SqliteTokenizer::Token> tokens;
- for (auto t = tokenizer.Next(); !t.str.empty(); t = tokenizer.Next()) {
+ for (auto t = tokenizer_.Next(); !t.str.empty(); t = tokenizer_.Next()) {
tokens.push_back(t);
}
return tokens;
}
+
+ private:
+ SqliteTokenizer tokenizer_{SqlSource::FromTraceProcessorImplementation("")};
};
TEST_F(SqliteTokenizerTest, EmptyString) {
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index 3be0a12..2fd2cdd 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -512,7 +512,7 @@
ASSERT_EQ(it.Status().message(),
R"(Traceback (most recent call last):
File "stdin" line 1 col 1
- select RUN_METRIC('foo/bar.sql');
+ select RUN_METRIC('foo/bar.sql')
^
Metric file "foo/bar.sql" line 1 col 8
select t from slice
@@ -534,7 +534,7 @@
ASSERT_EQ(it.Status().message(),
R"(Traceback (most recent call last):
File "stdin" line 1 col 1
- select IMPORT('foo.bar');
+ select IMPORT('foo.bar')
^
Module import "foo.bar" line 1 col 8
select t from slice
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 23e62ef..6a9edd9 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -74,7 +74,7 @@
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_flat_slice.h"
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h"
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h"
-#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_function.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/view.h"
#include "src/trace_processor/perfetto_sql/prelude/tables_views.h"
#include "src/trace_processor/perfetto_sql/stdlib/stdlib.h"
@@ -359,8 +359,8 @@
template <typename View>
void TraceProcessorImpl::RegisterView(const View& view) {
- RegisterTableFunction(std::unique_ptr<TableFunction>(
- new ViewTableFunction(&view, View::Name())));
+ RegisterStaticTableFunction(std::unique_ptr<StaticTableFunction>(
+ new ViewStaticTableFunction(&view, View::Name())));
}
TraceProcessorImpl::TraceProcessorImpl(const Config& cfg)
@@ -500,35 +500,35 @@
"stats", storage, SqliteTable::TableType::kEponymousOnly, false);
// Tables dynamically generated at query time.
- RegisterTableFunction(std::unique_ptr<ExperimentalFlamegraph>(
+ RegisterStaticTableFunction(std::unique_ptr<ExperimentalFlamegraph>(
new ExperimentalFlamegraph(&context_)));
- RegisterTableFunction(std::unique_ptr<ExperimentalCounterDur>(
+ RegisterStaticTableFunction(std::unique_ptr<ExperimentalCounterDur>(
new ExperimentalCounterDur(storage->counter_table())));
- RegisterTableFunction(std::unique_ptr<ExperimentalSliceLayout>(
+ RegisterStaticTableFunction(std::unique_ptr<ExperimentalSliceLayout>(
new ExperimentalSliceLayout(context_.storage.get()->mutable_string_pool(),
&storage->slice_table())));
- RegisterTableFunction(std::unique_ptr<Ancestor>(
+ RegisterStaticTableFunction(std::unique_ptr<Ancestor>(
new Ancestor(Ancestor::Type::kSlice, context_.storage.get())));
- RegisterTableFunction(std::unique_ptr<Ancestor>(new Ancestor(
+ RegisterStaticTableFunction(std::unique_ptr<Ancestor>(new Ancestor(
Ancestor::Type::kStackProfileCallsite, context_.storage.get())));
- RegisterTableFunction(std::unique_ptr<Ancestor>(
+ RegisterStaticTableFunction(std::unique_ptr<Ancestor>(
new Ancestor(Ancestor::Type::kSliceByStack, context_.storage.get())));
- RegisterTableFunction(std::unique_ptr<Descendant>(
+ RegisterStaticTableFunction(std::unique_ptr<Descendant>(
new Descendant(Descendant::Type::kSlice, context_.storage.get())));
- RegisterTableFunction(std::unique_ptr<Descendant>(
+ RegisterStaticTableFunction(std::unique_ptr<Descendant>(
new Descendant(Descendant::Type::kSliceByStack, context_.storage.get())));
- RegisterTableFunction(std::unique_ptr<ConnectedFlow>(new ConnectedFlow(
+ RegisterStaticTableFunction(std::unique_ptr<ConnectedFlow>(new ConnectedFlow(
ConnectedFlow::Mode::kDirectlyConnectedFlow, context_.storage.get())));
- RegisterTableFunction(std::unique_ptr<ConnectedFlow>(new ConnectedFlow(
+ RegisterStaticTableFunction(std::unique_ptr<ConnectedFlow>(new ConnectedFlow(
ConnectedFlow::Mode::kPrecedingFlow, context_.storage.get())));
- RegisterTableFunction(std::unique_ptr<ConnectedFlow>(new ConnectedFlow(
+ RegisterStaticTableFunction(std::unique_ptr<ConnectedFlow>(new ConnectedFlow(
ConnectedFlow::Mode::kFollowingFlow, context_.storage.get())));
- RegisterTableFunction(
+ RegisterStaticTableFunction(
std::unique_ptr<ExperimentalSchedUpid>(new ExperimentalSchedUpid(
storage->sched_slice_table(), storage->thread_table())));
- RegisterTableFunction(std::unique_ptr<ExperimentalAnnotatedStack>(
+ RegisterStaticTableFunction(std::unique_ptr<ExperimentalAnnotatedStack>(
new ExperimentalAnnotatedStack(&context_)));
- RegisterTableFunction(std::unique_ptr<ExperimentalFlatSlice>(
+ RegisterStaticTableFunction(std::unique_ptr<ExperimentalFlatSlice>(
new ExperimentalFlatSlice(&context_)));
// Views.
@@ -538,85 +538,85 @@
// Note: if adding a table here which might potentially contain many rows
// (O(rows in sched/slice/counter)), then consider calling ShrinkToFit on
// that table in TraceStorage::ShrinkToFitTables.
- RegisterDbTable(storage->arg_table());
- RegisterDbTable(storage->raw_table());
- RegisterDbTable(storage->ftrace_event_table());
- RegisterDbTable(storage->thread_table());
- RegisterDbTable(storage->process_table());
- RegisterDbTable(storage->filedescriptor_table());
+ RegisterStaticTable(storage->arg_table());
+ RegisterStaticTable(storage->raw_table());
+ RegisterStaticTable(storage->ftrace_event_table());
+ RegisterStaticTable(storage->thread_table());
+ RegisterStaticTable(storage->process_table());
+ RegisterStaticTable(storage->filedescriptor_table());
- RegisterDbTable(storage->slice_table());
- 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());
+ RegisterStaticTable(storage->slice_table());
+ RegisterStaticTable(storage->flow_table());
+ RegisterStaticTable(storage->slice_table());
+ RegisterStaticTable(storage->sched_slice_table());
+ RegisterStaticTable(storage->spurious_sched_wakeup_table());
+ RegisterStaticTable(storage->thread_state_table());
+ RegisterStaticTable(storage->gpu_slice_table());
- RegisterDbTable(storage->track_table());
- RegisterDbTable(storage->thread_track_table());
- RegisterDbTable(storage->process_track_table());
- RegisterDbTable(storage->cpu_track_table());
- RegisterDbTable(storage->gpu_track_table());
+ RegisterStaticTable(storage->track_table());
+ RegisterStaticTable(storage->thread_track_table());
+ RegisterStaticTable(storage->process_track_table());
+ RegisterStaticTable(storage->cpu_track_table());
+ RegisterStaticTable(storage->gpu_track_table());
- RegisterDbTable(storage->counter_table());
+ RegisterStaticTable(storage->counter_table());
- RegisterDbTable(storage->counter_track_table());
- RegisterDbTable(storage->process_counter_track_table());
- RegisterDbTable(storage->thread_counter_track_table());
- RegisterDbTable(storage->cpu_counter_track_table());
- RegisterDbTable(storage->irq_counter_track_table());
- RegisterDbTable(storage->softirq_counter_track_table());
- RegisterDbTable(storage->gpu_counter_track_table());
- RegisterDbTable(storage->gpu_counter_group_table());
- RegisterDbTable(storage->perf_counter_track_table());
- RegisterDbTable(storage->energy_counter_track_table());
- RegisterDbTable(storage->uid_counter_track_table());
- RegisterDbTable(storage->energy_per_uid_counter_track_table());
+ RegisterStaticTable(storage->counter_track_table());
+ RegisterStaticTable(storage->process_counter_track_table());
+ RegisterStaticTable(storage->thread_counter_track_table());
+ RegisterStaticTable(storage->cpu_counter_track_table());
+ RegisterStaticTable(storage->irq_counter_track_table());
+ RegisterStaticTable(storage->softirq_counter_track_table());
+ RegisterStaticTable(storage->gpu_counter_track_table());
+ RegisterStaticTable(storage->gpu_counter_group_table());
+ RegisterStaticTable(storage->perf_counter_track_table());
+ RegisterStaticTable(storage->energy_counter_track_table());
+ RegisterStaticTable(storage->uid_counter_track_table());
+ RegisterStaticTable(storage->energy_per_uid_counter_track_table());
- RegisterDbTable(storage->heap_graph_object_table());
- RegisterDbTable(storage->heap_graph_reference_table());
- RegisterDbTable(storage->heap_graph_class_table());
+ RegisterStaticTable(storage->heap_graph_object_table());
+ RegisterStaticTable(storage->heap_graph_reference_table());
+ RegisterStaticTable(storage->heap_graph_class_table());
- RegisterDbTable(storage->symbol_table());
- RegisterDbTable(storage->heap_profile_allocation_table());
- RegisterDbTable(storage->cpu_profile_stack_sample_table());
- RegisterDbTable(storage->perf_sample_table());
- RegisterDbTable(storage->stack_profile_callsite_table());
- RegisterDbTable(storage->stack_profile_mapping_table());
- RegisterDbTable(storage->stack_profile_frame_table());
- RegisterDbTable(storage->package_list_table());
- RegisterDbTable(storage->profiler_smaps_table());
+ RegisterStaticTable(storage->symbol_table());
+ RegisterStaticTable(storage->heap_profile_allocation_table());
+ RegisterStaticTable(storage->cpu_profile_stack_sample_table());
+ RegisterStaticTable(storage->perf_sample_table());
+ RegisterStaticTable(storage->stack_profile_callsite_table());
+ RegisterStaticTable(storage->stack_profile_mapping_table());
+ RegisterStaticTable(storage->stack_profile_frame_table());
+ RegisterStaticTable(storage->package_list_table());
+ RegisterStaticTable(storage->profiler_smaps_table());
- RegisterDbTable(storage->android_log_table());
- RegisterDbTable(storage->android_dumpstate_table());
- RegisterDbTable(storage->android_game_intervention_list_table());
+ RegisterStaticTable(storage->android_log_table());
+ RegisterStaticTable(storage->android_dumpstate_table());
+ RegisterStaticTable(storage->android_game_intervention_list_table());
- RegisterDbTable(storage->vulkan_memory_allocations_table());
+ RegisterStaticTable(storage->vulkan_memory_allocations_table());
- RegisterDbTable(storage->graphics_frame_slice_table());
+ RegisterStaticTable(storage->graphics_frame_slice_table());
- RegisterDbTable(storage->expected_frame_timeline_slice_table());
- RegisterDbTable(storage->actual_frame_timeline_slice_table());
+ RegisterStaticTable(storage->expected_frame_timeline_slice_table());
+ RegisterStaticTable(storage->actual_frame_timeline_slice_table());
- RegisterDbTable(storage->surfaceflinger_layers_snapshot_table());
- RegisterDbTable(storage->surfaceflinger_layer_table());
- RegisterDbTable(storage->surfaceflinger_transactions_table());
+ RegisterStaticTable(storage->surfaceflinger_layers_snapshot_table());
+ RegisterStaticTable(storage->surfaceflinger_layer_table());
+ RegisterStaticTable(storage->surfaceflinger_transactions_table());
- RegisterDbTable(storage->metadata_table());
- RegisterDbTable(storage->cpu_table());
- RegisterDbTable(storage->cpu_freq_table());
- RegisterDbTable(storage->clock_snapshot_table());
+ RegisterStaticTable(storage->metadata_table());
+ RegisterStaticTable(storage->cpu_table());
+ RegisterStaticTable(storage->cpu_freq_table());
+ RegisterStaticTable(storage->clock_snapshot_table());
- RegisterDbTable(storage->memory_snapshot_table());
- RegisterDbTable(storage->process_memory_snapshot_table());
- RegisterDbTable(storage->memory_snapshot_node_table());
- RegisterDbTable(storage->memory_snapshot_edge_table());
+ RegisterStaticTable(storage->memory_snapshot_table());
+ RegisterStaticTable(storage->process_memory_snapshot_table());
+ RegisterStaticTable(storage->memory_snapshot_node_table());
+ RegisterStaticTable(storage->memory_snapshot_edge_table());
- RegisterDbTable(storage->experimental_proto_path_table());
- RegisterDbTable(storage->experimental_proto_content_table());
+ RegisterStaticTable(storage->experimental_proto_path_table());
+ RegisterStaticTable(storage->experimental_proto_content_table());
- RegisterDbTable(storage->experimental_missing_chrome_processes_table());
+ RegisterStaticTable(storage->experimental_missing_chrome_processes_table());
}
TraceProcessorImpl::~TraceProcessorImpl() = default;
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 0cf6410..b869a63 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -108,12 +108,12 @@
friend class IteratorImpl;
template <typename Table>
- void RegisterDbTable(const Table& table) {
- engine_.RegisterTable(table, Table::Name());
+ void RegisterStaticTable(const Table& table) {
+ engine_.RegisterStaticTable(table, Table::Name());
}
- void RegisterTableFunction(std::unique_ptr<TableFunction> fn) {
- engine_.RegisterTableFunction(std::move(fn));
+ void RegisterStaticTableFunction(std::unique_ptr<StaticTableFunction> fn) {
+ engine_.RegisterStaticTableFunction(std::move(fn));
}
template <typename View>
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index ba49625..7c0380f 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -401,56 +401,38 @@
};
base::Status RunMetrics(const std::vector<MetricNameAndPath>& metrics,
- OutputFormat format,
- const google::protobuf::DescriptorPool& pool) {
+ OutputFormat format) {
std::vector<std::string> metric_names(metrics.size());
for (size_t i = 0; i < metrics.size(); ++i) {
metric_names[i] = metrics[i].name;
}
- if (format == OutputFormat::kTextProto) {
- std::string out;
- base::Status status =
- g_tp->ComputeMetricText(metric_names, TraceProcessor::kProtoText, &out);
- if (!status.ok()) {
- return status;
- }
- out += '\n';
- fwrite(out.c_str(), sizeof(char), out.size(), stdout);
- return base::OkStatus();
- }
-
- std::vector<uint8_t> metric_result;
- RETURN_IF_ERROR(g_tp->ComputeMetric(metric_names, &metric_result));
switch (format) {
- case OutputFormat::kJson: {
- // TODO(b/182165266): Handle this using ComputeMetricText.
- google::protobuf::DynamicMessageFactory factory(&pool);
- auto* descriptor =
- pool.FindMessageTypeByName("perfetto.protos.TraceMetrics");
- std::unique_ptr<google::protobuf::Message> metric_msg(
- factory.GetPrototype(descriptor)->New());
- metric_msg->ParseFromArray(metric_result.data(),
- static_cast<int>(metric_result.size()));
-
- // We need to instantiate field options from dynamic message factory
- // because otherwise it cannot parse our custom extensions.
- const google::protobuf::Message* field_options_prototype =
- factory.GetPrototype(
- pool.FindMessageTypeByName("google.protobuf.FieldOptions"));
- auto out = proto_to_json::MessageToJsonWithAnnotations(
- *metric_msg, field_options_prototype, 0);
- fwrite(out.c_str(), sizeof(char), out.size(), stdout);
- break;
- }
- case OutputFormat::kBinaryProto:
+ case OutputFormat::kBinaryProto: {
+ std::vector<uint8_t> metric_result;
+ RETURN_IF_ERROR(g_tp->ComputeMetric(metric_names, &metric_result));
fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(),
stdout);
break;
+ }
+ case OutputFormat::kJson: {
+ std::string out;
+ RETURN_IF_ERROR(g_tp->ComputeMetricText(
+ metric_names, TraceProcessor::MetricResultFormat::kJson, &out));
+ out += '\n';
+ fwrite(out.c_str(), sizeof(char), out.size(), stdout);
+ break;
+ }
+ case OutputFormat::kTextProto: {
+ std::string out;
+ RETURN_IF_ERROR(g_tp->ComputeMetricText(
+ metric_names, TraceProcessor::MetricResultFormat::kProtoText, &out));
+ out += '\n';
+ fwrite(out.c_str(), sizeof(char), out.size(), stdout);
+ break;
+ }
case OutputFormat::kNone:
break;
- case OutputFormat::kTextProto:
- PERFETTO_FATAL("This case was already handled.");
}
return base::OkStatus();
@@ -1495,7 +1477,7 @@
}
base::Status status =
- RunMetrics(options.metrics, options.metric_format, *options.pool);
+ RunMetrics(options.metrics, options.metric_format);
if (!status.ok()) {
fprintf(stderr, "%s\n", status.c_message());
}
@@ -1660,7 +1642,7 @@
OutputFormat metric_format = ParseOutputFormat(options);
if (!metrics.empty()) {
- RETURN_IF_ERROR(RunMetrics(metrics, metric_format, pool));
+ RETURN_IF_ERROR(RunMetrics(metrics, metric_format));
}
if (!options.query_file_path.empty()) {
diff --git a/test/trace_processor/diff_tests/android/tests.py b/test/trace_processor/diff_tests/android/tests.py
index b035f18..0565554 100644
--- a/test/trace_processor/diff_tests/android/tests.py
+++ b/test/trace_processor/diff_tests/android/tests.py
@@ -14,10 +14,10 @@
# limitations under the License.
from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
from python.generators.diff_tests.testing import DiffTestBlueprint
from python.generators.diff_tests.testing import TestSuite
-
+from python.generators.diff_tests.testing import PrintProfileProto
class Android(TestSuite):
@@ -480,6 +480,37 @@
query=Metric('android_monitor_contention'),
out=Path('android_monitor_contention.out'))
+ def test_monitor_contention_graph(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_monitor_contention_trace.atr'),
+ query="""
+ SELECT IMPORT('android.monitor_contention');
+
+ SELECT HEX(pprof) FROM android_monitor_contention_graph(303)
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 29604
+ Stack:
+ android.bg:android.os.MessageQueue.nativeWake (0x0)
+ fg:android.os.MessageQueue.next (0x0)
+
+ Sample:
+ Values: 66924
+ Stack:
+ android.bg:android.os.MessageQueue.enqueueMessage (0x0)
+ fg:android.os.MessageQueue.next (0x0)
+
+ Sample:
+ Values: 73265
+ Stack:
+ main:android.os.MessageQueue.enqueueMessage (0x0)
+ fg:android.os.MessageQueue.next (0x0)
+ """))
+
def test_thread_creation_spam(self):
return DiffTestBlueprint(
trace=DataPath('android_monitor_contention_trace.atr'),
@@ -685,3 +716,312 @@
"AIDL::cpp::IInstalld::prepareAppProfile::cppServer","system_server","/system/bin/installd","system_server",641,548,1,-1000,-1000,25281131360,25281145719
"AIDL::cpp::IInstalld::prepareAppProfile::cppServer","system_server","/system/bin/installd","system_server",641,548,1,-1000,-1000,25281273755,25281315273
"""))
+
+ def test_binder_outgoing_graph(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_binder_metric_trace.atr'),
+ query="""
+ SELECT IMPORT('android.binder');
+ SELECT HEX(pprof) FROM ANDROID_BINDER_OUTGOING_GRAPH(259)
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 0
+ Stack:
+ /system/bin/surfaceflinger (0x0)
+ binder:446_1 (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ stealReceiveChannel (0x0)
+ IDisplayEventConnection (0x0)
+ /system/bin/surfaceflinger (0x0)
+ binder:446_1 (0x0)
+ """))
+
+ def test_binder_incoming_graph(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_binder_metric_trace.atr'),
+ query="""
+ SELECT IMPORT('android.binder');
+ SELECT HEX(pprof) FROM ANDROID_BINDER_INCOMING_GRAPH(296)
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 1764197
+ Stack:
+ fixupAppData (0x0)
+ IInstalld (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 202423
+ Stack:
+ rmdex (0x0)
+ IInstalld (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 438512
+ Stack:
+ cleanupInvalidPackageDirs (0x0)
+ IInstalld (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 4734897
+ Stack:
+ invalidateMounts (0x0)
+ IInstalld (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 7448312
+ Stack:
+ prepareAppProfile (0x0)
+ IInstalld (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 91238713
+ Stack:
+ createAppDataBatched (0x0)
+ IInstalld (0x0)
+ system_server (0x0)
+ """))
+
+ def test_binder_graph_invalid_oom(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_binder_metric_trace.atr'),
+ query="""
+ SELECT IMPORT('android.binder');
+ SELECT HEX(pprof) FROM ANDROID_BINDER_GRAPH(2000, 2000, 2000, 2000)
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ """))
+
+ def test_binder_graph_valid_oom(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_binder_metric_trace.atr'),
+ query="""
+ SELECT IMPORT('android.binder');
+ SELECT HEX(pprof) FROM ANDROID_BINDER_GRAPH(-1000, 1000, -1000, 1000)
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 0
+ Stack:
+ /system/bin/apexd (0x0)
+ /system/bin/servicemanager (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ /system/bin/bootanimation (0x0)
+ /system/bin/surfaceflinger (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ /system/bin/cameraserver (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ /system/bin/storaged (0x0)
+ /vendor/bin/hw/android.hardware.health-service.cuttlefish (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ /system/bin/surfaceflinger (0x0)
+ /system/bin/bootanimation (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ /system/bin/surfaceflinger (0x0)
+ /vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ media.metrics (0x0)
+ /system/bin/audioserver (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ system_server (0x0)
+ /system/bin/servicemanager (0x0)
+
+ Sample:
+ Values: 0
+ Stack:
+ system_server (0x0)
+ /system/bin/surfaceflinger (0x0)
+
+ Sample:
+ Values: 105827054
+ Stack:
+ /system/bin/installd (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 11316
+ Stack:
+ system_server (0x0)
+ /apex/com.android.os.statsd/bin/statsd (0x0)
+
+ Sample:
+ Values: 12567639
+ Stack:
+ /system/bin/servicemanager (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 137623
+ Stack:
+ /vendor/bin/hw/android.hardware.lights-service.example (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 140719
+ Stack:
+ system_server (0x0)
+ /system/bin/storaged (0x0)
+
+ Sample:
+ Values: 150044
+ Stack:
+ /vendor/bin/hw/android.hardware.input.processor-service.example (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 1877718
+ Stack:
+ /system/bin/surfaceflinger (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 19303
+ Stack:
+ system_server (0x0)
+ /vendor/bin/hw/android.hardware.sensors-service.example (0x0)
+
+ Sample:
+ Values: 210889
+ Stack:
+ /system/bin/servicemanager (0x0)
+ /apex/com.android.os.statsd/bin/statsd (0x0)
+
+ Sample:
+ Values: 21505514
+ Stack:
+ /system/bin/idmap2d (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 25394
+ Stack:
+ /system/bin/servicemanager (0x0)
+ /system/bin/surfaceflinger (0x0)
+
+ Sample:
+ Values: 2552696
+ Stack:
+ /system/bin/hwservicemanager (0x0)
+ /system/bin/cameraserver (0x0)
+
+ Sample:
+ Values: 273686
+ Stack:
+ /vendor/bin/hw/android.hardware.sensors-service.example (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 28045
+ Stack:
+ /apex/com.android.os.statsd/bin/statsd (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 297647
+ Stack:
+ /system/bin/hwservicemanager (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 3483649
+ Stack:
+ system_server (0x0)
+ /system/bin/audioserver (0x0)
+
+ Sample:
+ Values: 3677545
+ Stack:
+ /system/bin/servicemanager (0x0)
+ /system/bin/audioserver (0x0)
+
+ Sample:
+ Values: 3991341
+ Stack:
+ /system/bin/servicemanager (0x0)
+ /system/bin/cameraserver (0x0)
+
+ Sample:
+ Values: 41164
+ Stack:
+ system_server (0x0)
+ /vendor/bin/hw/android.hardware.health-service.cuttlefish (0x0)
+
+ Sample:
+ Values: 4948091
+ Stack:
+ system_server (0x0)
+ /system/bin/cameraserver (0x0)
+
+ Sample:
+ Values: 502254
+ Stack:
+ /vendor/bin/hw/android.hardware.health-service.cuttlefish (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 629626
+ Stack:
+ /apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 78428525
+ Stack:
+ /vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu (0x0)
+ /system/bin/surfaceflinger (0x0)
+
+ Sample:
+ Values: 81216
+ Stack:
+ /system/bin/vold (0x0)
+ system_server (0x0)
+
+ Sample:
+ Values: 837989
+ Stack:
+ /system/bin/servicemanager (0x0)
+ /system/bin/storaged (0x0)
+ """))
diff --git a/test/trace_processor/diff_tests/functions/tests.py b/test/trace_processor/diff_tests/functions/tests.py
index 25ce166..64adb47 100644
--- a/test/trace_processor/diff_tests/functions/tests.py
+++ b/test/trace_processor/diff_tests/functions/tests.py
@@ -17,27 +17,9 @@
from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
from python.generators.diff_tests.testing import DiffTestBlueprint
from python.generators.diff_tests.testing import TestSuite
+from python.generators.diff_tests.testing import PrintProfileProto
from google.protobuf import text_format
-
-def PrintProfileProto(profile):
- locations = {l.id: l for l in profile.location}
- functions = {f.id: f for f in profile.function}
- samples = []
- for s in profile.sample:
- stack = []
- for location in [locations[id] for id in s.location_id]:
- for function in [functions[l.function_id] for l in location.line]:
- stack.append("{name} ({address})".format(
- name=profile.string_table[function.name],
- address=hex(location.address)))
- if len(location.line) == 0:
- stack.append("({address})".format(address=hex(location.address)))
- samples.append('Sample:\nValues: {values}\nStack:\n{stack}'.format(
- values=', '.join(map(str, s.value)), stack='\n'.join(stack)))
- return '\n\n'.join(sorted(samples)) + '\n'
-
-
class Functions(TestSuite):
def test_create_function(self):
diff --git a/test/trace_processor/diff_tests/tables/tests_sched.py b/test/trace_processor/diff_tests/tables/tests_sched.py
index 92bbe64..00facf8 100644
--- a/test/trace_processor/diff_tests/tables/tests_sched.py
+++ b/test/trace_processor/diff_tests/tables/tests_sched.py
@@ -353,9 +353,75 @@
trace=DataPath('sched_wakeup_trace.atr'),
query="""
SELECT IMPORT('experimental.thread_executing_span');
- SELECT EXPERIMENTAL_THREAD_EXECUTING_SPAN_ID_FROM_THREAD_STATE_ID(13120) AS thread_executing_span_id
+ SELECT EXPERIMENTAL_THREAD_EXECUTING_SPAN_ID_FROM_THREAD_STATE_ID(15173) AS thread_executing_span_id
""",
out=Csv("""
"thread_executing_span_id"
"[NULL]"
"""))
+
+ def test_thread_executing_span_following_from_sleep_thread_state(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT EXPERIMENTAL_THREAD_EXECUTING_SPAN_FOLLOWING_THREAD_STATE_ID(15173) AS thread_executing_span_id
+ """,
+ out=Csv("""
+ "thread_executing_span_id"
+ 15750
+ """))
+
+ def test_thread_executing_span_following_from_non_sleep_thread_state(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT EXPERIMENTAL_THREAD_EXECUTING_SPAN_FOLLOWING_THREAD_STATE_ID(12394) AS thread_executing_span_id
+ """,
+ out=Csv("""
+ "thread_executing_span_id"
+ "[NULL]"
+ """))
+
+ def test_thread_executing_span_critical_path(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT
+ ts,
+ dur,
+ tid,
+ pid,
+ thread_name,
+ process_name,
+ waker_thread_name,
+ waker_process_name,
+ blocked_dur,
+ blocked_state,
+ blocked_function,
+ height,
+ is_leaf,
+ leaf_ts,
+ leaf_blocked_dur,
+ leaf_blocked_state,
+ leaf_blocked_function
+ FROM EXPERIMENTAL_THREAD_EXECUTING_SPAN_CRITICAL_PATH(EXPERIMENTAL_THREAD_EXECUTING_SPAN_FOLLOWING_THREAD_STATE_ID(15173))
+ ORDER BY ts
+ """,
+ out=Csv("""
+ "ts","dur","tid","pid","thread_name","process_name","waker_thread_name","waker_process_name","blocked_dur","blocked_state","blocked_function","height","is_leaf","leaf_ts","leaf_blocked_dur","leaf_blocked_state","leaf_blocked_function"
+ 1737555644935,155300703,281,243,"binder:243_4","/system/bin/vold","StorageManagerS","system_server",207137317,"S","[NULL]",11,0,1737716642304,160997369,"S","[NULL]"
+ 1737710945638,719567,158,1,"init","/system/bin/init","binder:243_4","/system/bin/vold",320099853,"S","[NULL]",10,0,1737716642304,160997369,"S","[NULL]"
+ 1737711665205,2066552,281,243,"binder:243_4","/system/bin/vold","init","/system/bin/init",473986,"S","[NULL]",9,0,1737716642304,160997369,"S","[NULL]"
+ 1737713731757,46394,3335,3335,"kworker/u4:2","kworker/u4:2-events_unbound","binder:243_4","/system/bin/vold",172402014,"I","worker_thread",8,0,1737716642304,160997369,"S","[NULL]"
+ 1737713778151,818659,281,243,"binder:243_4","/system/bin/vold","kworker/u4:2","kworker/u4:2-events_unbound",38815,"D","__flush_work",7,0,1737716642304,160997369,"S","[NULL]"
+ 1737714596810,414789,743,642,"StorageManagerS","system_server","binder:243_4","/system/bin/vold",161843036,"S","[NULL]",6,0,1737716642304,160997369,"S","[NULL]"
+ 1737715011599,256989,3501,3487,"binder:3487_4","com.android.providers.media.module","StorageManagerS","system_server",167350508,"S","[NULL]",5,0,1737716642304,160997369,"S","[NULL]"
+ 1737715268588,219727,3519,3487,"android.bg","com.android.providers.media.module","binder:3487_4","com.android.providers.media.module",163900842,"S","[NULL]",4,0,1737716642304,160997369,"S","[NULL]"
+ 1737715488315,357472,657,642,"binder:642_1","system_server","android.bg","com.android.providers.media.module",344488980,"S","[NULL]",3,0,1737716642304,160997369,"S","[NULL]"
+ 1737715845787,497587,743,642,"StorageManagerS","system_server","binder:642_1","system_server",793525,"S","[NULL]",2,0,1737716642304,160997369,"S","[NULL]"
+ 1737716343374,298930,3501,3487,"binder:3487_4","com.android.providers.media.module","StorageManagerS","system_server",1016895,"S","[NULL]",1,0,1737716642304,160997369,"S","[NULL]"
+ 1737716642304,4521857,3487,3487,"rs.media.module","com.android.providers.media.module","binder:3487_4","com.android.providers.media.module",160997369,"S","[NULL]",0,0,1737716642304,160997369,"S","[NULL]"
+ """))
diff --git a/ui/src/base/comparison_utils.ts b/ui/src/base/comparison_utils.ts
index ea6110a..842c812 100644
--- a/ui/src/base/comparison_utils.ts
+++ b/ui/src/base/comparison_utils.ts
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {ColumnType} from '../common/query_result';
-
export type ComparisonFn<X> = (a: X, b: X) => number;
export type SortDirection = 'DESC'|'ASC';
@@ -39,7 +37,7 @@
};
}
-export type SortableValue = ColumnType|undefined;
+export type SortableValue = string|number|bigint|null|Uint8Array|undefined;
function columnTypeKind(a: SortableValue): number {
if (a === undefined) {
diff --git a/ui/src/base/json_utils.ts b/ui/src/base/json_utils.ts
new file mode 100644
index 0000000..69d97d0
--- /dev/null
+++ b/ui/src/base/json_utils.ts
@@ -0,0 +1,23 @@
+// 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.
+
+// Similar to JSON.stringify() but supports bigints.
+// Bigints are simply serialized to a string, so the original object cannot be
+// recovered with JSON.parse(), as bigints will turn into strings.
+// Useful for e.g. tracing, where string arg values are required.
+export function stringifyJsonWithBigints(object: any): string {
+ return JSON.stringify(
+ object,
+ (_, value) => typeof value === 'bigint' ? value.toString() : value);
+}
diff --git a/ui/src/base/json_utils_unittest.ts b/ui/src/base/json_utils_unittest.ts
new file mode 100644
index 0000000..540e03c
--- /dev/null
+++ b/ui/src/base/json_utils_unittest.ts
@@ -0,0 +1,21 @@
+// 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.
+
+import {stringifyJsonWithBigints} from './json_utils';
+
+test('stringifyJsonWithBigints', () => {
+ const obj = {foo: 'bar', baz: 123n};
+ const expected = '{"foo":"bar","baz":"123"}';
+ expect(stringifyJsonWithBigints(obj)).toEqual(expected);
+});
diff --git a/ui/src/chrome_extension/chrome_tracing_controller.ts b/ui/src/chrome_extension/chrome_tracing_controller.ts
index 42f2a48..9ed3930 100644
--- a/ui/src/chrome_extension/chrome_tracing_controller.ts
+++ b/ui/src/chrome_extension/chrome_tracing_controller.ts
@@ -18,17 +18,17 @@
import {base64Encode} from '../base/string_utils';
import {
- browserSupportsPerfettoConfig,
- extractTraceConfig,
- hasSystemDataSourceConfig,
-} from '../base/trace_config_utils';
-import {TraceConfig} from '../common/protos';
-import {
ConsumerPortResponse,
GetTraceStatsResponse,
ReadBuffersResponse,
} from '../controller/consumer_port_types';
import {RpcConsumerPort} from '../controller/record_controller_interfaces';
+import {TraceConfig} from '../core/protos';
+import {
+ browserSupportsPerfettoConfig,
+ extractTraceConfig,
+ hasSystemDataSourceConfig,
+} from '../core/trace_config_utils';
import {perfetto} from '../gen/protos';
import {DevToolsSocket} from './devtools_socket';
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index 7bbde09..5546ea2 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -15,16 +15,16 @@
import {defer, Deferred} from '../base/deferred';
import {assertExists, assertTrue} from '../base/logging';
import {Span, Time} from '../common/time';
-import {perfetto} from '../gen/protos';
-
-import {ProtoRingBuffer} from './proto_ring_buffer';
import {
ComputeMetricArgs,
ComputeMetricResult,
DisableAndReadMetatraceResult,
QueryArgs,
ResetTraceProcessorArgs,
-} from './protos';
+} from '../core/protos';
+import {perfetto} from '../gen/protos';
+
+import {ProtoRingBuffer} from './proto_ring_buffer';
import {
createQueryResult,
LONG,
diff --git a/ui/src/common/http_rpc_engine.ts b/ui/src/common/http_rpc_engine.ts
index ddeec7c..f9db060 100644
--- a/ui/src/common/http_rpc_engine.ts
+++ b/ui/src/common/http_rpc_engine.ts
@@ -14,7 +14,7 @@
import {fetchWithTimeout} from '../base/http_utils';
import {assertExists} from '../base/logging';
-import {StatusResult} from '../common/protos';
+import {StatusResult} from '../core/protos';
import {Engine, LoadingTracker} from './engine';
diff --git a/ui/src/common/metatracing.ts b/ui/src/common/metatracing.ts
index 68bec99..99c5ea9 100644
--- a/ui/src/common/metatracing.ts
+++ b/ui/src/common/metatracing.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {PerfettoMetatrace, Trace, TracePacket} from '../common/protos';
+import {PerfettoMetatrace, Trace, TracePacket} from '../core/protos';
import {perfetto} from '../gen/protos';
import {featureFlags} from './feature_flags';
@@ -163,3 +163,31 @@
traceEvents.shift();
}
}
+
+// Flatten arbitrary values so they can be used as args in traceEvent() et al.
+export function flattenArgs(
+ input: unknown, parentKey = ''): {[key: string]: string} {
+ if (typeof input !== 'object' || input === null) {
+ return {[parentKey]: String(input)};
+ }
+
+ if (Array.isArray(input)) {
+ const result: Record<string, string> = {};
+
+ (input as Array<unknown>).forEach((item, index) => {
+ const arrayKey = `${parentKey}[${index}]`;
+ Object.assign(result, flattenArgs(item, arrayKey));
+ });
+
+ return result;
+ }
+
+ const result: Record<string, string> = {};
+
+ Object.entries(input as Record<string, unknown>).forEach(([key, value]) => {
+ const newKey = parentKey ? `${parentKey}.${key}` : key;
+ Object.assign(result, flattenArgs(value, newKey));
+ });
+
+ return result;
+}
diff --git a/ui/src/common/metatracing_unittest.ts b/ui/src/common/metatracing_unittest.ts
new file mode 100644
index 0000000..215b77d
--- /dev/null
+++ b/ui/src/common/metatracing_unittest.ts
@@ -0,0 +1,56 @@
+// 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.
+
+import {flattenArgs} from './metatracing';
+
+describe('flattenArgs', () => {
+ test('flattens nested object', () => {
+ const value = {
+ foo: {
+ bar: [1, 2, 3],
+ },
+ baz: {baz: 'qux'},
+ };
+ expect(flattenArgs(value)).toStrictEqual({
+ 'foo.bar[0]': '1',
+ 'foo.bar[1]': '2',
+ 'foo.bar[2]': '3',
+ 'baz.baz': 'qux',
+ });
+ });
+
+ test('flattens single value', () => {
+ const value = 123;
+ expect(flattenArgs(value)).toStrictEqual({
+ '': '123',
+ });
+ });
+
+ test('flattens array', () => {
+ const value = [1, 2, 3];
+ expect(flattenArgs(value)).toStrictEqual({
+ '[0]': '1',
+ '[1]': '2',
+ '[2]': '3',
+ });
+ });
+
+ test('flattens array of objects', () => {
+ const value = [{foo: 'bar'}, {baz: 123}];
+ expect(flattenArgs(value)).toStrictEqual({
+ '[0].foo': 'bar',
+ '[1].baz': '123',
+ });
+ });
+});
diff --git a/ui/src/common/protos_unittest.ts b/ui/src/common/protos_unittest.ts
index 181917a..174e350 100644
--- a/ui/src/common/protos_unittest.ts
+++ b/ui/src/common/protos_unittest.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TraceConfig} from './protos';
+import {TraceConfig} from '../core/protos';
test('round trip config proto', () => {
const input = TraceConfig.create({
diff --git a/ui/src/common/recordingV2/chrome_traced_tracing_session.ts b/ui/src/common/recordingV2/chrome_traced_tracing_session.ts
index e36a5aa..a42528b 100644
--- a/ui/src/common/recordingV2/chrome_traced_tracing_session.ts
+++ b/ui/src/common/recordingV2/chrome_traced_tracing_session.ts
@@ -33,7 +33,8 @@
IBufferStats,
ISlice,
TraceConfig,
-} from '../protos';
+} from '../../core/protos';
+
import {RecordingError} from './recording_error_handling';
import {
TracingSession,
diff --git a/ui/src/common/recordingV2/recording_config_utils.ts b/ui/src/common/recordingV2/recording_config_utils.ts
index 139227e..9524670 100644
--- a/ui/src/common/recordingV2/recording_config_utils.ts
+++ b/ui/src/common/recordingV2/recording_config_utils.ts
@@ -15,7 +15,6 @@
import {base64Encode} from '../../base/string_utils';
import {RecordConfig} from '../../controller/record_config_types';
-import {perfetto} from '../../gen/protos';
import {
AndroidLogConfig,
AndroidLogId,
@@ -36,7 +35,8 @@
TraceConfig,
TrackEventConfig,
VmstatCounters,
-} from '../protos';
+} from '../../core/protos';
+import {perfetto} from '../../gen/protos';
import {TargetInfo} from './recording_interfaces_v2';
diff --git a/ui/src/common/recordingV2/recording_interfaces_v2.ts b/ui/src/common/recordingV2/recording_interfaces_v2.ts
index 974d277..ca187e2 100644
--- a/ui/src/common/recordingV2/recording_interfaces_v2.ts
+++ b/ui/src/common/recordingV2/recording_interfaces_v2.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TraceConfig} from '../protos';
+import {TraceConfig} from '../../core/protos';
// TargetFactory connects, disconnects and keeps track of targets.
// There is one factory for AndroidWebusb, AndroidWebsocket, Chrome etc.
diff --git a/ui/src/common/recordingV2/recording_page_controller.ts b/ui/src/common/recordingV2/recording_page_controller.ts
index 438843d..c42130c 100644
--- a/ui/src/common/recordingV2/recording_page_controller.ts
+++ b/ui/src/common/recordingV2/recording_page_controller.ts
@@ -13,6 +13,7 @@
// limitations under the License.
import {assertExists, assertTrue} from '../../base/logging';
+import {TraceConfig} from '../../core/protos';
import {raf} from '../../core/raf_scheduler';
import {globals} from '../../frontend/globals';
import {autosaveConfigStore} from '../../frontend/record_config';
@@ -25,7 +26,6 @@
} from '../../frontend/recording/reset_interface_modal';
import {Actions} from '../actions';
import {TRACE_SUFFIX} from '../constants';
-import {TraceConfig} from '../protos';
import {currentDateHourAndMinute} from '../time';
import {genTraceConfig} from './recording_config_utils';
diff --git a/ui/src/common/recordingV2/traced_tracing_session.ts b/ui/src/common/recordingV2/traced_tracing_session.ts
index 0b1f656..a49c780 100644
--- a/ui/src/common/recordingV2/traced_tracing_session.ts
+++ b/ui/src/common/recordingV2/traced_tracing_session.ts
@@ -34,7 +34,7 @@
ReadBuffersRequest,
ReadBuffersResponse,
TraceConfig,
-} from '../protos';
+} from '../../core/protos';
import {RecordingError} from './recording_error_handling';
import {
diff --git a/ui/src/controller/adb_base_controller.ts b/ui/src/controller/adb_base_controller.ts
index bbafbc3..144614e 100644
--- a/ui/src/controller/adb_base_controller.ts
+++ b/ui/src/controller/adb_base_controller.ts
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {extractDurationFromTraceConfig} from '../base/trace_config_utils';
-import {extractTraceConfig} from '../base/trace_config_utils';
import {isAdbTarget} from '../common/state';
+import {
+ extractDurationFromTraceConfig,
+ extractTraceConfig,
+} from '../core/trace_config_utils';
import {globals} from '../frontend/globals';
import {Adb} from './adb_interfaces';
diff --git a/ui/src/controller/adb_shell_controller.ts b/ui/src/controller/adb_shell_controller.ts
index 4c7b6a2..d2fe620 100644
--- a/ui/src/controller/adb_shell_controller.ts
+++ b/ui/src/controller/adb_shell_controller.ts
@@ -15,7 +15,7 @@
import {_TextDecoder} from 'custom_utils';
import {base64Encode} from '../base/string_utils';
-import {extractTraceConfig} from '../base/trace_config_utils';
+import {extractTraceConfig} from '../core/trace_config_utils';
import {AdbBaseConsumerPort, AdbConnectionState} from './adb_base_controller';
import {Adb, AdbStream} from './adb_interfaces';
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index e8cb6dd..32d5e75 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -17,10 +17,6 @@
import {base64Encode} from '../base/string_utils';
import {Actions} from '../common/actions';
import {TRACE_SUFFIX} from '../common/constants';
-import {
- ConsumerPort,
- TraceConfig,
-} from '../common/protos';
import {genTraceConfig} from '../common/recordingV2/recording_config_utils';
import {TargetInfo} from '../common/recordingV2/recording_interfaces_v2';
import {
@@ -29,6 +25,10 @@
isChromeTarget,
RecordingTarget,
} from '../common/state';
+import {
+ ConsumerPort,
+ TraceConfig,
+} from '../core/protos';
import {globals} from '../frontend/globals';
import {publishBufferUsage, publishTrackData} from '../frontend/publish';
diff --git a/ui/src/controller/record_controller_jsdomtest.ts b/ui/src/controller/record_controller_jsdomtest.ts
index bc7c15b..55411ec 100644
--- a/ui/src/controller/record_controller_jsdomtest.ts
+++ b/ui/src/controller/record_controller_jsdomtest.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {assertExists} from '../base/logging';
-import {TraceConfig} from '../common/protos';
+import {TraceConfig} from '../core/protos';
import {createEmptyRecordConfig} from './record_config_types';
import {genConfigProto, toPbtxt} from './record_controller';
diff --git a/ui/src/common/protos.ts b/ui/src/core/protos.ts
similarity index 99%
rename from ui/src/common/protos.ts
rename to ui/src/core/protos.ts
index fa83fac..c3fdaca 100644
--- a/ui/src/common/protos.ts
+++ b/ui/src/core/protos.ts
@@ -86,12 +86,12 @@
BatteryCounters,
BufferConfig,
ChromeConfig,
- ConsumerPort,
ComputeMetricArgs,
ComputeMetricResult,
+ ConsumerPort,
DataSourceConfig,
- DisableAndReadMetatraceResult,
DataSourceDescriptor,
+ DisableAndReadMetatraceResult,
DisableTracingRequest,
DisableTracingResponse,
EnableTracingRequest,
@@ -116,21 +116,21 @@
MeminfoCounters,
NativeContinuousDumpConfig,
NetworkPacketTraceConfig,
- ProcessStatsConfig,
PerfettoMetatrace,
PerfEventConfig,
- ReadBuffersRequest,
- ReadBuffersResponse,
+ ProcessStatsConfig,
+ QueryArgs,
QueryServiceStateRequest,
QueryServiceStateResponse,
- QueryArgs,
+ ReadBuffersRequest,
+ ReadBuffersResponse,
ResetTraceProcessorArgs,
StatCounters,
StatusResult,
SysStatsConfig,
Trace,
TraceConfig,
- TrackEventConfig,
TracePacket,
+ TrackEventConfig,
VmstatCounters,
};
diff --git a/ui/src/base/trace_config_utils.ts b/ui/src/core/trace_config_utils.ts
similarity index 97%
rename from ui/src/base/trace_config_utils.ts
rename to ui/src/core/trace_config_utils.ts
index 3a08963..e7e6daf 100644
--- a/ui/src/base/trace_config_utils.ts
+++ b/ui/src/core/trace_config_utils.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TraceConfig} from '../common/protos';
+import {TraceConfig} from '../core/protos';
import {perfetto} from '../gen/protos';
// In this file are contained a few functions to simplify the proto parsing.
diff --git a/ui/src/frontend/bottom_tab.ts b/ui/src/frontend/bottom_tab.ts
index f063152..5a42b5f 100644
--- a/ui/src/frontend/bottom_tab.ts
+++ b/ui/src/frontend/bottom_tab.ts
@@ -15,6 +15,7 @@
import m from 'mithril';
import {v4 as uuidv4} from 'uuid';
+import {stringifyJsonWithBigints} from '../base/json_utils';
import {Actions} from '../common/actions';
import {EngineProxy} from '../common/engine';
import {traceEvent} from '../common/metatracing';
@@ -236,9 +237,7 @@
'uuid': uuid,
'kind': args.kind,
'tag': args.tag ?? '<undefined>',
- 'config': JSON.stringify(
- args.config,
- (_, value) => typeof value === 'bigint' ? value.toString() : value),
+ 'config': stringifyJsonWithBigints(args.config),
},
});
}
diff --git a/ui/src/frontend/chrome_slice_details_tab.ts b/ui/src/frontend/chrome_slice_details_tab.ts
index 058633d..2bf320f 100644
--- a/ui/src/frontend/chrome_slice_details_tab.ts
+++ b/ui/src/frontend/chrome_slice_details_tab.ts
@@ -65,6 +65,10 @@
return slice.process?.pid;
}
+function getUpidFromSlice(slice: SliceDetails): number|undefined {
+ return slice.process?.upid;
+}
+
function getProcessNameFromSlice(slice: SliceDetails): string|undefined {
return slice.process?.name;
}
@@ -139,44 +143,11 @@
name: 'Lock graph',
shouldDisplay: (slice: SliceDetails) => hasId(slice),
run: (slice: SliceDetails) => runQueryInNewTab(
- `SELECT IMPORT('android.monitor_contention');
- DROP TABLE IF EXISTS FAST;
- CREATE TABLE FAST
- AS
- WITH slice_process AS (
- SELECT process.name, process.upid FROM slice
- JOIN thread_track ON thread_track.id = slice.track_id
- JOIN thread USING(utid)
- JOIN process USING(upid)
- WHERE slice.id = ${slice.id}
- )
- SELECT *,
- IIF(blocked_thread_name LIKE 'binder:%', 'binder', blocked_thread_name)
- AS blocked_thread_name_norm,
- IIF(blocking_thread_name LIKE 'binder:%', 'binder', blocking_thread_name)
- AS blocking_thread_name_norm
- FROM android_monitor_contention_chain, slice_process
- WHERE android_monitor_contention_chain.upid = slice_process.upid;
+ `
+ SELECT IMPORT('android.monitor_contention');
- WITH
- R AS (
- SELECT
- id,
- dur,
- CAT_STACKS(blocked_thread_name_norm || ':' || short_blocked_method,
- blocking_thread_name_norm || ':' || short_blocking_method) AS stack
- FROM FAST
- WHERE parent_id IS NULL
- UNION ALL
- SELECT
- c.id,
- c.dur AS dur,
- CAT_STACKS(stack, blocking_thread_name_norm || ':' || short_blocking_method) AS stack
- FROM FAST c, R AS p
- WHERE p.id = c.parent_id
- )
- SELECT TITLE.process_name, EXPERIMENTAL_PROFILE(stack, 'duration', 'ns', dur) AS pprof
- FROM R, (SELECT process_name FROM FAST LIMIT 1) TITLE;`,
+ SELECT * FROM android_monitor_contention_graph(${getUpidFromSlice(slice)});
+ `,
'Lock graph',
),
},
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index bd2e3c1..caa483e 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -25,6 +25,7 @@
import {CommandManager} from '../common/commands';
import {createEmptyState} from '../common/empty_state';
import {RECORDING_V2_FLAG} from '../common/feature_flags';
+import {flattenArgs, traceEvent} from '../common/metatracing';
import {pluginManager, pluginRegistry} from '../common/plugins';
import {State} from '../common/state';
import {initWasm} from '../common/wasm_engine_proxy';
@@ -86,9 +87,13 @@
dispatchMultiple(actions: DeferredAction[]) {
const edits = actions.map((action) => {
- return (draft: Draft<State>) => {
- (StateActions as any)[action.type](draft, action.args);
- };
+ return traceEvent(`action.${action.type}`, () => {
+ return (draft: Draft<State>) => {
+ (StateActions as any)[action.type](draft, action.args);
+ };
+ }, {
+ args: flattenArgs(action.args),
+ });
});
globals.store.edit(edits);
}
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 63c2bd8..09ed04f 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -201,10 +201,12 @@
this.repositionCanvas();
this.trash.add(new SimpleResizeObserver(dom, () => {
- this.readParentSizeFromDom(dom);
- this.updateCanvasDimensions();
- this.repositionCanvas();
- raf.scheduleFullRedraw();
+ const parentSizeChanged = this.readParentSizeFromDom(dom);
+ if (parentSizeChanged) {
+ this.updateCanvasDimensions();
+ this.repositionCanvas();
+ this.redrawCanvas();
+ }
}));
// TODO(dproy): Handle change in doesScroll attribute.
diff --git a/ui/src/frontend/recording/android_settings.ts b/ui/src/frontend/recording/android_settings.ts
index 4e41ac1..6568187 100644
--- a/ui/src/frontend/recording/android_settings.ts
+++ b/ui/src/frontend/recording/android_settings.ts
@@ -14,7 +14,7 @@
import m from 'mithril';
-import {DataSourceDescriptor} from '../../common/protos';
+import {DataSourceDescriptor} from '../../core/protos';
import {globals} from '../globals';
import {
Dropdown,
diff --git a/ui/src/frontend/recording/memory_settings.ts b/ui/src/frontend/recording/memory_settings.ts
index 002d6db..1db2b75 100644
--- a/ui/src/frontend/recording/memory_settings.ts
+++ b/ui/src/frontend/recording/memory_settings.ts
@@ -14,7 +14,7 @@
import m from 'mithril';
-import {MeminfoCounters, VmstatCounters} from '../../common/protos';
+import {MeminfoCounters, VmstatCounters} from '../../core/protos';
import {globals} from '../globals';
import {
Dropdown,
diff --git a/ui/src/frontend/rpc_http_dialog.ts b/ui/src/frontend/rpc_http_dialog.ts
index 7bd0a5f..024a113 100644
--- a/ui/src/frontend/rpc_http_dialog.ts
+++ b/ui/src/frontend/rpc_http_dialog.ts
@@ -17,7 +17,7 @@
import {assertExists} from '../base/logging';
import {Actions} from '../common/actions';
import {HttpRpcEngine, RPC_URL} from '../common/http_rpc_engine';
-import {StatusResult} from '../common/protos';
+import {StatusResult} from '../core/protos';
import {VERSION} from '../gen/perfetto_version';
import {perfetto} from '../gen/protos';
diff --git a/ui/src/plugins/com.example.Skeleton/OWNERS b/ui/src/plugins/com.example.Skeleton/OWNERS
new file mode 100644
index 0000000..1acc35b
--- /dev/null
+++ b/ui/src/plugins/com.example.Skeleton/OWNERS
@@ -0,0 +1 @@
+# SKELETON: Add your email to this file!
diff --git a/ui/src/plugins/com.example.Skeleton/index.ts b/ui/src/plugins/com.example.Skeleton/index.ts
new file mode 100644
index 0000000..c7735c0
--- /dev/null
+++ b/ui/src/plugins/com.example.Skeleton/index.ts
@@ -0,0 +1,41 @@
+// 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.
+
+import {
+ EngineProxy,
+ PluginContext,
+ Store,
+ TracePlugin,
+} from '../../public';
+
+interface State {}
+
+// SKELETON: Rename this class to match your plugin.
+class Skeleton implements TracePlugin {
+ static migrate(_initialState: unknown): State {
+ return {};
+ }
+
+ constructor(_store: Store<State>, _engine: EngineProxy) {}
+
+ dispose(): void {}
+}
+
+export const plugin = {
+ // SKELETON: Update pluginId to match the directory of the plugin.
+ pluginId: 'com.example.Skeleton',
+ activate: (ctx: PluginContext) => {
+ ctx.registerTracePluginFactory(Skeleton);
+ },
+};