Merge "Trace Redaction - Expand 'scrub ftrace events' test coverage" into main
diff --git a/Android.bp b/Android.bp
index 3233f92..e30b0d7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11122,6 +11122,7 @@
"src/trace_processor/importers/common/event_tracker.cc",
"src/trace_processor/importers/common/flow_tracker.cc",
"src/trace_processor/importers/common/global_args_tracker.cc",
+ "src/trace_processor/importers/common/jit_cache.cc",
"src/trace_processor/importers/common/mapping_tracker.cc",
"src/trace_processor/importers/common/metadata_tracker.cc",
"src/trace_processor/importers/common/process_tracker.cc",
@@ -11449,6 +11450,7 @@
"src/trace_processor/importers/proto/chrome_system_probes_module.cc",
"src/trace_processor/importers/proto/chrome_system_probes_parser.cc",
"src/trace_processor/importers/proto/default_modules.cc",
+ "src/trace_processor/importers/proto/jit_tracker.cc",
"src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
"src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
"src/trace_processor/importers/proto/metadata_minimal_module.cc",
@@ -11489,6 +11491,7 @@
srcs: [
"src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc",
"src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc",
+ "src/trace_processor/importers/proto/jit_tracker_unittest.cc",
"src/trace_processor/importers/proto/network_trace_module_unittest.cc",
"src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc",
"src/trace_processor/importers/proto/profile_packet_sequence_state_unittest.cc",
@@ -11870,6 +11873,7 @@
filegroup {
name: "perfetto_src_trace_processor_perfetto_sql_intrinsics_functions_functions",
srcs: [
+ "src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_function.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_view_function.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/import.cc",
@@ -11939,6 +11943,7 @@
"src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
],
}
@@ -11969,6 +11974,7 @@
"src/trace_processor/tables/android_tables.py",
"src/trace_processor/tables/counter_tables.py",
"src/trace_processor/tables/flow_tables.py",
+ "src/trace_processor/tables/jit_tables.py",
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
@@ -12047,6 +12053,7 @@
"src/trace_processor/perfetto_sql/stdlib/common/args.sql",
"src/trace_processor/perfetto_sql/stdlib/common/counters.sql",
"src/trace_processor/perfetto_sql/stdlib/common/cpus.sql",
+ "src/trace_processor/perfetto_sql/stdlib/common/jit.sql",
"src/trace_processor/perfetto_sql/stdlib/common/metadata.sql",
"src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql",
"src/trace_processor/perfetto_sql/stdlib/common/slices.sql",
@@ -12065,6 +12072,7 @@
"src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql",
"src/trace_processor/perfetto_sql/stdlib/graphs/dominator_tree.sql",
"src/trace_processor/perfetto_sql/stdlib/graphs/search.sql",
+ "src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql",
"src/trace_processor/perfetto_sql/stdlib/intervals/overlap.sql",
"src/trace_processor/perfetto_sql/stdlib/linux/cpu_idle.sql",
"src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql",
@@ -12086,6 +12094,7 @@
"src/trace_processor/perfetto_sql/stdlib/slices/slices.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql",
"src/trace_processor/perfetto_sql/stdlib/time/conversion.sql",
+ "src/trace_processor/perfetto_sql/stdlib/v8/jit.sql",
],
cmd: "$(location tools/gen_amalgamated_sql.py) --namespace=stdlib --cpp-out=$(out) $(in)",
out: [
@@ -12246,6 +12255,7 @@
"src/trace_processor/tables/android_tables.py",
"src/trace_processor/tables/counter_tables.py",
"src/trace_processor/tables/flow_tables.py",
+ "src/trace_processor/tables/jit_tables.py",
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
@@ -12264,6 +12274,7 @@
"src/trace_processor/tables/android_tables_py.h",
"src/trace_processor/tables/counter_tables_py.h",
"src/trace_processor/tables/flow_tables_py.h",
+ "src/trace_processor/tables/jit_tables_py.h",
"src/trace_processor/tables/memory_tables_py.h",
"src/trace_processor/tables/metadata_tables_py.h",
"src/trace_processor/tables/profiler_tables_py.h",
@@ -12286,6 +12297,7 @@
"src/trace_processor/tables/android_tables.py",
"src/trace_processor/tables/counter_tables.py",
"src/trace_processor/tables/flow_tables.py",
+ "src/trace_processor/tables/jit_tables.py",
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
diff --git a/BUILD b/BUILD
index 1b3d23f..9bf8bc8 100644
--- a/BUILD
+++ b/BUILD
@@ -1474,6 +1474,8 @@
"src/trace_processor/importers/common/flow_tracker.h",
"src/trace_processor/importers/common/global_args_tracker.cc",
"src/trace_processor/importers/common/global_args_tracker.h",
+ "src/trace_processor/importers/common/jit_cache.cc",
+ "src/trace_processor/importers/common/jit_cache.h",
"src/trace_processor/importers/common/mapping_tracker.cc",
"src/trace_processor/importers/common/mapping_tracker.h",
"src/trace_processor/importers/common/metadata_tracker.cc",
@@ -1839,6 +1841,8 @@
"src/trace_processor/importers/proto/chrome_system_probes_parser.h",
"src/trace_processor/importers/proto/default_modules.cc",
"src/trace_processor/importers/proto/default_modules.h",
+ "src/trace_processor/importers/proto/jit_tracker.cc",
+ "src/trace_processor/importers/proto/jit_tracker.h",
"src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
"src/trace_processor/importers/proto/memory_tracker_snapshot_module.h",
"src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
@@ -2233,6 +2237,8 @@
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_intrinsics_functions_functions",
srcs = [
+ "src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc",
+ "src/trace_processor/perfetto_sql/intrinsics/functions/base64.h",
"src/trace_processor/perfetto_sql/intrinsics/functions/clock_functions.h",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_function.cc",
"src/trace_processor/perfetto_sql/intrinsics/functions/create_function.h",
@@ -2314,6 +2320,8 @@
"src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.h",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h",
],
@@ -2408,6 +2416,7 @@
"src/trace_processor/perfetto_sql/stdlib/common/args.sql",
"src/trace_processor/perfetto_sql/stdlib/common/counters.sql",
"src/trace_processor/perfetto_sql/stdlib/common/cpus.sql",
+ "src/trace_processor/perfetto_sql/stdlib/common/jit.sql",
"src/trace_processor/perfetto_sql/stdlib/common/metadata.sql",
"src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql",
"src/trace_processor/perfetto_sql/stdlib/common/slices.sql",
@@ -2461,6 +2470,7 @@
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_intervals_intervals",
srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql",
"src/trace_processor/perfetto_sql/stdlib/intervals/overlap.sql",
],
)
@@ -2541,6 +2551,14 @@
],
)
+# GN target: //src/trace_processor/perfetto_sql/stdlib/v8:v8
+perfetto_filegroup(
+ name = "src_trace_processor_perfetto_sql_stdlib_v8_v8",
+ srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/v8/jit.sql",
+ ],
+)
+
# GN target: //src/trace_processor/perfetto_sql/stdlib:stdlib
perfetto_cc_amalgamated_sql(
name = "src_trace_processor_perfetto_sql_stdlib_stdlib",
@@ -2562,6 +2580,7 @@
":src_trace_processor_perfetto_sql_stdlib_sched_utilization_utilization",
":src_trace_processor_perfetto_sql_stdlib_slices_slices",
":src_trace_processor_perfetto_sql_stdlib_time_time",
+ ":src_trace_processor_perfetto_sql_stdlib_v8_v8",
],
outs = [
"src/trace_processor/perfetto_sql/stdlib/stdlib.h",
@@ -2669,6 +2688,7 @@
"src/trace_processor/tables/android_tables.py",
"src/trace_processor/tables/counter_tables.py",
"src/trace_processor/tables/flow_tables.py",
+ "src/trace_processor/tables/jit_tables.py",
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
@@ -2683,6 +2703,7 @@
"src/trace_processor/tables/android_tables_py.h",
"src/trace_processor/tables/counter_tables_py.h",
"src/trace_processor/tables/flow_tables_py.h",
+ "src/trace_processor/tables/jit_tables_py.h",
"src/trace_processor/tables/memory_tables_py.h",
"src/trace_processor/tables/metadata_tables_py.h",
"src/trace_processor/tables/profiler_tables_py.h",
diff --git a/bazel/deps.bzl b/bazel/deps.bzl
index a105a63..ee17856 100644
--- a/bazel/deps.bzl
+++ b/bazel/deps.bzl
@@ -35,18 +35,18 @@
_add_repo_if_not_existing(
http_archive,
name = "perfetto_dep_sqlite",
- url = "https://storage.googleapis.com/perfetto/sqlite-amalgamation-3390200.zip",
- sha256 = "87775784f8b22d0d0f1d7811870d39feaa7896319c7c20b849a4181c5a50609b",
- strip_prefix = "sqlite-amalgamation-3390200",
+ url = "https://storage.googleapis.com/perfetto/sqlite-amalgamation-3440200.zip",
+ sha256 = "833be89b53b3be8b40a2e3d5fedb635080e3edb204957244f3d6987c2bb2345f",
+ strip_prefix = "sqlite-amalgamation-3440200",
build_file = "//bazel:sqlite.BUILD",
)
_add_repo_if_not_existing(
http_archive,
name = "perfetto_dep_sqlite_src",
- url = "https://storage.googleapis.com/perfetto/sqlite-src-3390200.zip",
- sha256 = "e933d77000f45f3fbc8605f0050586a3013505a8de9b44032bd00ed72f1586f0",
- strip_prefix = "sqlite-src-3390200",
+ url = "https://storage.googleapis.com/perfetto/sqlite-src-3440200.zip",
+ sha256 = "73187473feb74509357e8fa6cb9fd67153b2d010d00aeb2fddb6ceeb18abaf27",
+ strip_prefix = "sqlite-src-3440200",
build_file = "//bazel:sqlite.BUILD",
)
diff --git a/gn/standalone/sanitizers/BUILD.gn b/gn/standalone/sanitizers/BUILD.gn
index 574dcfb..4fef817 100644
--- a/gn/standalone/sanitizers/BUILD.gn
+++ b/gn/standalone/sanitizers/BUILD.gn
@@ -19,8 +19,9 @@
visibility = [ "*" ]
if (using_sanitizer) {
public_configs = [ ":sanitizers_ldflags" ]
+ deps = [ ":ignorelist_copy" ]
if (is_android && sanitizer_lib != "" && !sanitizer_lib_dir_is_static) {
- deps = [ ":copy_sanitizer_lib" ]
+ deps += [ ":copy_sanitizer_lib" ]
}
}
}
@@ -32,9 +33,20 @@
}
}
+# Add a dependency on the ignorelist.txt file to cause rebuilds when
+# the file changes.
+copy("ignorelist_copy") {
+ sources = [ "ignorelist.txt" ]
+ outputs = [ "${target_out_dir}/ignorelist.txt" ]
+}
+
config("sanitizers_cflags") {
if (using_sanitizer) {
- cflags = [ "-fno-omit-frame-pointer" ]
+ ignorelist_path_ = rebase_path("ignorelist.txt", root_build_dir)
+ cflags = [
+ "-fno-omit-frame-pointer",
+ "-fsanitize-ignorelist=$ignorelist_path_",
+ ]
defines = []
if (is_asan) {
diff --git a/gn/standalone/sanitizers/ignorelist.txt b/gn/standalone/sanitizers/ignorelist.txt
new file mode 100644
index 0000000..8a3d1d1
--- /dev/null
+++ b/gn/standalone/sanitizers/ignorelist.txt
@@ -0,0 +1,4 @@
+# The rules in this file are only applied at compile time.
+[memory]
+fun:vdbeChangeP4Full
+fun:*OpenDiskFile*
diff --git a/python/generators/sql_processing/utils.py b/python/generators/sql_processing/utils.py
index 539115e..1a76e1f 100644
--- a/python/generators/sql_processing/utils.py
+++ b/python/generators/sql_processing/utils.py
@@ -110,6 +110,7 @@
ALLOWED_PREFIXES = {
'counters': 'counter',
'chrome/util': 'cr',
+ 'intervals': 'interval',
'graphs': 'graph',
'slices': 'slice'
}
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index fba722d..a1fa238 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -433,7 +433,7 @@
static_assert(sizeof(decltype(file_size.QuadPart)) <= sizeof(uint64_t));
return static_cast<uint64_t>(file_size.QuadPart);
#else
- struct stat buf;
+ struct stat buf {};
if (fstat(fd, &buf) == -1) {
return std::nullopt;
}
diff --git a/src/trace_processor/importers/common/BUILD.gn b/src/trace_processor/importers/common/BUILD.gn
index 5108dca..c3ba78a 100644
--- a/src/trace_processor/importers/common/BUILD.gn
+++ b/src/trace_processor/importers/common/BUILD.gn
@@ -37,6 +37,8 @@
"flow_tracker.h",
"global_args_tracker.cc",
"global_args_tracker.h",
+ "jit_cache.cc",
+ "jit_cache.h",
"mapping_tracker.cc",
"mapping_tracker.h",
"metadata_tracker.cc",
diff --git a/src/trace_processor/importers/common/jit_cache.cc b/src/trace_processor/importers/common/jit_cache.cc
new file mode 100644
index 0000000..8380177
--- /dev/null
+++ b/src/trace_processor/importers/common/jit_cache.cc
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/common/jit_cache.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/jit_tables_py.h"
+#include "src/trace_processor/tables/metadata_tables_py.h"
+#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+std::pair<FrameId, bool> JitCache::JittedFunction::InternFrame(
+ TraceProcessorContext* context,
+ FrameKey frame_key) {
+ if (FrameId* id = interned_frames_.Find(frame_key); id) {
+ return {*id, false};
+ }
+
+ FrameId frame_id =
+ context->storage->mutable_stack_profile_frame_table()
+ ->Insert({context->storage->jit_code_table()
+ .FindById(jit_code_id_)
+ ->function_name(),
+ frame_key.mapping_id,
+ static_cast<int64_t>(frame_key.rel_pc), symbol_set_id_})
+ .id;
+ interned_frames_.Insert(frame_key, frame_id);
+ context->stack_profile_tracker->OnFrameCreated(frame_id);
+
+ context->storage->mutable_jit_frame_table()->Insert({jit_code_id_, frame_id});
+
+ return {frame_id, true};
+}
+
+tables::JitCodeTable::Id JitCache::LoadCode(
+ int64_t timestamp,
+ UniqueTid utid,
+ AddressRange code_range,
+ StringId function_name,
+ std::optional<SourceLocation> source_location,
+ TraceBlobView native_code) {
+ PERFETTO_CHECK(range_.Contains(code_range));
+ PERFETTO_CHECK(context_->storage->thread_table()
+ .FindById(tables::ThreadTable::Id(utid))
+ ->upid() == upid_);
+
+ PERFETTO_CHECK(native_code.size() == 0 ||
+ native_code.size() == code_range.size());
+
+ std::optional<uint32_t> symbol_set_id;
+ if (source_location.has_value()) {
+ // TODO(carlscab): Remove duplication via new SymbolTracker class
+ symbol_set_id = context_->storage->symbol_table().row_count();
+ context_->storage->mutable_symbol_table()->Insert(
+ {*symbol_set_id, function_name, source_location->file_name,
+ source_location->line_number});
+ }
+
+ auto* jit_code_table = context_->storage->mutable_jit_code_table();
+ const auto jit_code_id =
+ jit_code_table
+ ->Insert({timestamp, std::nullopt, utid,
+ static_cast<int64_t>(code_range.start()),
+ static_cast<int64_t>(code_range.size()), function_name,
+ Base64Encode(native_code)})
+ .id;
+
+ functions_.DeleteOverlapsAndEmplace(
+ [&](std::pair<const AddressRange, JittedFunction>& entry) {
+ jit_code_table->FindById(entry.second.jit_code_id())
+ ->set_estimated_delete_ts(timestamp);
+ },
+ code_range, jit_code_id, symbol_set_id);
+
+ return jit_code_id;
+}
+
+std::pair<FrameId, bool> JitCache::InternFrame(VirtualMemoryMapping* mapping,
+ uint64_t rel_pc,
+ base::StringView function_name) {
+ FrameKey key{mapping->mapping_id(), rel_pc};
+
+ if (auto it = functions_.Find(mapping->ToAddress(rel_pc));
+ it != functions_.end()) {
+ return it->second.InternFrame(context_, key);
+ }
+
+ if (FrameId* id = unknown_frames_.Find(key); id) {
+ return {*id, false};
+ }
+
+ context_->storage->IncrementStats(stats::jit_unknown_frame);
+
+ FrameId id =
+ context_->storage->mutable_stack_profile_frame_table()
+ ->Insert({context_->storage->InternString(
+ function_name.empty()
+ ? base::StringView(
+ "[+" + base::Uint64ToHexString(rel_pc) + "]")
+ : function_name),
+ key.mapping_id, static_cast<int64_t>(rel_pc)})
+ .id;
+ unknown_frames_.Insert(key, id);
+ return {id, true};
+}
+
+UserMemoryMapping& JitCache::CreateMapping() {
+ CreateMappingParams params;
+ params.memory_range = range_;
+ params.name = "[jit: " + name_ + "]";
+ return context_->mapping_tracker->CreateUserMemoryMapping(upid_,
+ std::move(params));
+}
+
+StringId JitCache::Base64Encode(const TraceBlobView& data) {
+ return context_->storage->InternString(
+ base::StringView(base::Base64Encode(data.data(), data.size())));
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/common/jit_cache.h b/src/trace_processor/importers/common/jit_cache.h
new file mode 100644
index 0000000..19205b6
--- /dev/null
+++ b/src/trace_processor/importers/common/jit_cache.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_JIT_CACHE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_JIT_CACHE_H_
+
+#include <cstdint>
+#include <optional>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/jit_tables_py.h"
+#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class VirtualMemoryMapping;
+class UserMemoryMapping;
+
+// JitCache represents a container of jit code generated by the same VM. Jitted
+// functions can be "added" to the cache and stack frames added to the
+// StackFrameTracker that lie in the code range for such a function will
+// automatically be resolved (associate function name and source location).
+//
+// Jitted functions can also be deleted from the cache by overwriting existing
+// ones.
+class JitCache {
+ public:
+ struct SourceLocation {
+ StringId file_name;
+ uint32_t line_number;
+ };
+
+ JitCache(TraceProcessorContext* context,
+ std::string name,
+ UniquePid upid,
+ AddressRange range)
+ : context_(context), name_(std::move(name)), upid_(upid), range_(range) {}
+
+ // Notify the cache that a jitted function was loaded at the given address
+ // range. Any existing function that fully or partially overlaps with the new
+ // function will be deleted.
+ // The passed in listener will be notified each time a new Frame is created
+ // for this function.
+ tables::JitCodeTable::Id LoadCode(
+ int64_t timestamp,
+ UniqueTid utid,
+ AddressRange code_range,
+ StringId function_name,
+ std::optional<SourceLocation> source_location,
+ TraceBlobView native_code);
+
+ // Forward frame interning request.
+ // MappingTracker allows other trackers to register ranges of memory for
+ // which they need to control when a new frame is created. Jitted code can
+ // move in memory over time, so the same program counter might refer to
+ // different functions at different point in time. MappingTracker does
+ // not keep track of such moves but instead delegates the creation of jitted
+ // frames to a delegate.
+ // Returns frame_id, and whether a new row as created or not.
+ std::pair<FrameId, bool> InternFrame(VirtualMemoryMapping* mapping,
+ uint64_t rel_pc,
+ base::StringView function_name);
+
+ // Simpleperf does not emit mmap events for jitted ranges (actually for non
+ // file backed executable mappings). So have a way to generate a mapping on
+ // the fly for FindMapping requests in a jitted region with no associated
+ // mapping.
+ UserMemoryMapping& CreateMapping();
+
+ private:
+ struct FrameKey {
+ tables::StackProfileMappingTable::Id mapping_id;
+ uint64_t rel_pc;
+ struct Hasher {
+ size_t operator()(const FrameKey& k) const {
+ return static_cast<size_t>(
+ base::Hasher::Combine(k.mapping_id.value, k.rel_pc));
+ }
+ };
+ bool operator==(const FrameKey& other) const {
+ return mapping_id == other.mapping_id && rel_pc == other.rel_pc;
+ }
+ };
+
+ class JittedFunction {
+ public:
+ JittedFunction(tables::JitCodeTable::Id jit_code_id,
+ std::optional<uint32_t> symbol_set_id)
+ : jit_code_id_(jit_code_id), symbol_set_id_(symbol_set_id) {}
+
+ tables::JitCodeTable::Id jit_code_id() const { return jit_code_id_; }
+
+ std::pair<FrameId, bool> InternFrame(TraceProcessorContext* context,
+ FrameKey frame_key);
+
+ private:
+ const tables::JitCodeTable::Id jit_code_id_;
+ const std::optional<uint32_t> symbol_set_id_;
+ base::FlatHashMap<FrameKey, FrameId, FrameKey::Hasher> interned_frames_;
+ };
+
+ StringId Base64Encode(const TraceBlobView& data);
+
+ TraceProcessorContext* const context_;
+ const std::string name_;
+ const UniquePid upid_;
+ const AddressRange range_;
+ AddressRangeMap<JittedFunction> functions_;
+ base::FlatHashMap<FrameKey, FrameId, FrameKey::Hasher> unknown_frames_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_JIT_CACHE_H_
diff --git a/src/trace_processor/importers/common/mapping_tracker.cc b/src/trace_processor/importers/common/mapping_tracker.cc
index 13b8274..0dec3e5 100644
--- a/src/trace_processor/importers/common/mapping_tracker.cc
+++ b/src/trace_processor/importers/common/mapping_tracker.cc
@@ -21,8 +21,10 @@
#include <memory>
#include <utility>
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/jit_cache.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "src/trace_processor/util/build_id.h"
@@ -31,14 +33,13 @@
namespace trace_processor {
namespace {
-bool IsKernelModule(base::StringView name) {
- return !name.StartsWith("[kernel.kallsyms]");
+bool IsKernelModule(const CreateMappingParams& params) {
+ return !base::StartsWith(params.name, "[kernel.kallsyms]") &&
+ !params.memory_range.empty();
}
} // namespace
-JitDelegate::~JitDelegate() = default;
-
template <typename MappingImpl>
MappingImpl& MappingTracker::AddMapping(std::unique_ptr<MappingImpl> mapping) {
auto ptr = mapping.get();
@@ -57,7 +58,7 @@
// TODO(carlscab): Guess build_id if not provided. Some tools like simpleperf
// add a mapping file_name ->build_id that we could use here
- const bool is_module = IsKernelModule(base::StringView(params.name));
+ const bool is_module = IsKernelModule(params);
if (!is_module && kernel_ != nullptr) {
PERFETTO_CHECK(params.memory_range == kernel_->memory_range());
@@ -68,10 +69,8 @@
new KernelMemoryMapping(context_, std::move(params)));
if (is_module) {
- // TODO(carlscab): Overlaps not supported (for now?). Should be fine for
- // kernel.
- PERFETTO_CHECK(
- kernel_modules_.Emplace(mapping->memory_range(), mapping.get()));
+ kernel_modules_.DeleteOverlapsAndEmplace(mapping->memory_range(),
+ mapping.get());
} else {
kernel_ = mapping.get();
}
@@ -88,15 +87,15 @@
const AddressRange mapping_range = params.memory_range;
std::unique_ptr<UserMemoryMapping> mapping(
new UserMemoryMapping(context_, upid, std::move(params)));
- // TODO(carlscab): Overlaps not supported (for now?).
- PERFETTO_CHECK(user_memory_[upid].Emplace(mapping_range, mapping.get()));
- jit_delegates_[upid].ForOverlaps(
- mapping_range, [&](std::pair<const AddressRange, JitDelegate*>& entry) {
+ user_memory_[upid].DeleteOverlapsAndEmplace(mapping_range, mapping.get());
+
+ jit_caches_[upid].ForOverlaps(
+ mapping_range, [&](std::pair<const AddressRange, JitCache*>& entry) {
const auto& jit_range = entry.first;
- JitDelegate* jit_delegate = entry.second;
+ JitCache* jit_cache = entry.second;
PERFETTO_CHECK(jit_range.Contains(mapping_range));
- mapping->SetJitDelegate(jit_delegate);
+ mapping->SetJitCache(jit_cache);
});
return AddMapping(std::move(mapping));
@@ -122,9 +121,9 @@
}
}
- if (auto* delegates = jit_delegates_.Find(upid); delegates) {
+ if (auto* delegates = jit_caches_.Find(upid); delegates) {
if (auto it = delegates->Find(address); it != delegates->end()) {
- return it->second->CreateMapping();
+ return &it->second->CreateMapping();
}
}
@@ -155,13 +154,13 @@
void MappingTracker::AddJitRange(UniquePid upid,
AddressRange jit_range,
- JitDelegate* delegate) {
+ JitCache* jit_cache) {
// TODO(carlscab): Deal with overlaps
- jit_delegates_[upid].DeleteOverlapsAndEmplace(jit_range, delegate);
+ jit_caches_[upid].DeleteOverlapsAndEmplace(jit_range, jit_cache);
user_memory_[upid].ForOverlaps(
jit_range, [&](std::pair<const AddressRange, UserMemoryMapping*>& entry) {
PERFETTO_CHECK(jit_range.Contains(entry.first));
- entry.second->SetJitDelegate(delegate);
+ entry.second->SetJitCache(jit_cache);
});
}
diff --git a/src/trace_processor/importers/common/mapping_tracker.h b/src/trace_processor/importers/common/mapping_tracker.h
index 95dc355..bc45bef 100644
--- a/src/trace_processor/importers/common/mapping_tracker.h
+++ b/src/trace_processor/importers/common/mapping_tracker.h
@@ -35,31 +35,7 @@
namespace perfetto {
namespace trace_processor {
-// Api used to forward frame interning requests for frames that fall in a
-// jitted memory region.
-// MappingTracker allows other trackers to register ranges of memory for
-// which they need to control when a new frame is created. Jitted code can
-// move in memory over time, so the same program counter might refer to
-// different functions at different point in time. MappingTracker does
-// not keep track of such moves but instead delegates the creation of jitted
-// frames to a delegate.
-class JitDelegate {
- public:
- virtual ~JitDelegate();
- // Forward frame interning request.
- // Implementations are free to intern the frame as needed.
- // Returns frame_id, and whether a new row as created or not.
- virtual std::pair<FrameId, bool> InternFrame(
- VirtualMemoryMapping* mapping,
- uint64_t rel_pc,
- base::StringView function_name) = 0;
-
- // Simpleperf does not emit mmap events for jitted ranges (actually for non
- // file backed executable mappings). So have a way to generate a mapping on
- // the fly for FindMapping requests in a jitted region with no associated
- // mapping.
- virtual UserMemoryMapping* CreateMapping() = 0;
-};
+class JitCache;
// Keeps track of all aspects relative to memory mappings.
// This class keeps track of 3 types of mappings: UserMemoryMapping,
@@ -113,7 +89,7 @@
// If the added region overlaps with other existing ranges the latter are all
// deleted.
// Jitted ranges will only be applied to UserMemoryMappings
- void AddJitRange(UniquePid upid, AddressRange range, JitDelegate* delegate);
+ void AddJitRange(UniquePid upid, AddressRange range, JitCache* jit_cache);
private:
template <typename MappingImpl>
@@ -159,7 +135,7 @@
AddressRangeMap<KernelMemoryMapping*> kernel_modules_;
KernelMemoryMapping* kernel_ = nullptr;
- base::FlatHashMap<UniquePid, AddressRangeMap<JitDelegate*>> jit_delegates_;
+ base::FlatHashMap<UniquePid, AddressRangeMap<JitCache*>> jit_caches_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/common/virtual_memory_mapping.cc b/src/trace_processor/importers/common/virtual_memory_mapping.cc
index 60166f59..0485243 100644
--- a/src/trace_processor/importers/common/virtual_memory_mapping.cc
+++ b/src/trace_processor/importers/common/virtual_memory_mapping.cc
@@ -25,7 +25,7 @@
#include "perfetto/ext/base/string_view.h"
#include "src/trace_processor/importers/common/address_range.h"
-#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/jit_cache.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
@@ -84,8 +84,8 @@
FrameId VirtualMemoryMapping::InternFrame(uint64_t rel_pc,
base::StringView function_name) {
auto [frame_id, was_inserted] =
- jit_delegate_ ? jit_delegate_->InternFrame(this, rel_pc, function_name)
- : InternFrameImpl(rel_pc, function_name);
+ jit_cache_ ? jit_cache_->InternFrame(this, rel_pc, function_name)
+ : InternFrameImpl(rel_pc, function_name);
if (was_inserted) {
frames_by_rel_pc_[rel_pc].push_back(frame_id);
context_->stack_profile_tracker->OnFrameCreated(frame_id);
diff --git a/src/trace_processor/importers/common/virtual_memory_mapping.h b/src/trace_processor/importers/common/virtual_memory_mapping.h
index 7b8ef58..498a9ef 100644
--- a/src/trace_processor/importers/common/virtual_memory_mapping.h
+++ b/src/trace_processor/importers/common/virtual_memory_mapping.h
@@ -39,7 +39,7 @@
// TODO(carlscab): Reconsider whether jit is the best abstraction here. All we
// really care is about mapping a `rel_pc` to a symbol (aka symbolization) and
// whether is this is constant.
-class JitDelegate;
+class JitCache;
// Represents a mapping in virtual memory.
class VirtualMemoryMapping {
@@ -61,13 +61,18 @@
const std::optional<BuildId>& build_id() const { return build_id_; }
// Whether this maps to a region that holds jitted code.
- bool is_jitted() const { return jit_delegate_ != nullptr; }
+ bool is_jitted() const { return jit_cache_ != nullptr; }
// Converts an absolute address into a relative one.
uint64_t ToRelativePc(uint64_t address) const {
return address - memory_range_.start() + offset_ + load_bias_;
}
+ // Converts a relative address to an absolute one.
+ uint64_t ToAddress(uint64_t rel_pc) const {
+ return rel_pc + (memory_range_.start() - offset_ - load_bias_);
+ }
+
// Creates a frame for the given `rel_pc`. Note that if the mapping
// `is_jitted()` same `rel_pc` values can return different mappings (as jitted
// functions can be created and deleted over time.) So for such mappings the
@@ -87,9 +92,7 @@
std::pair<FrameId, bool> InternFrameImpl(uint64_t rel_pc,
base::StringView function_name);
- void SetJitDelegate(JitDelegate* jit_delegate) {
- jit_delegate_ = jit_delegate;
- }
+ void SetJitCache(JitCache* jit_cache) { jit_cache_ = jit_cache; }
TraceProcessorContext* const context_;
const MappingId mapping_id_;
@@ -98,7 +101,7 @@
const uint64_t load_bias_;
const std::string name_;
std::optional<BuildId> const build_id_;
- JitDelegate* jit_delegate_ = nullptr;
+ JitCache* jit_cache_ = nullptr;
struct FrameKey {
uint64_t rel_pc;
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index c853c44..69f1b58 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -88,8 +88,7 @@
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
namespace {
@@ -571,6 +570,7 @@
// buffer ABI not matching the data read out of the kernel (while the trace
// was being recorded). Reject such traces altogether as we need to make such
// errors hard to ignore (most likely it's a bug in perfetto or the kernel).
+ using protos::pbzero::FtraceParseStatus;
auto error_it = evt.ftrace_parse_errors();
if (error_it) {
auto dev_flag =
@@ -585,13 +585,26 @@
"native trace_processor_shell as an accelerator with these flags: "
"\"trace_processor_shell --httpd --dev --dev-flag "
"ignore-ftrace-parse-errors=true <trace_file.pb>\". Errors: ";
+ size_t error_count = 0;
for (; error_it; ++error_it) {
- msg += protos::pbzero::FtraceParseStatus_Name(
- static_cast<protos::pbzero::FtraceParseStatus>(*error_it));
+ auto error_code = static_cast<FtraceParseStatus>(*error_it);
+ // Relax the strictness of zero-padded page errors, they're prevalent
+ // but also do not affect the actual ftrace payload.
+ // See b/329396486#comment6, b/204564312#comment20.
+ if (error_code ==
+ FtraceParseStatus::FTRACE_STATUS_ABI_ZERO_DATA_LENGTH) {
+ context_->storage->IncrementStats(
+ stats::ftrace_abi_errors_skipped_zero_data_length);
+ continue;
+ }
+ error_count += 1;
+ msg += protos::pbzero::FtraceParseStatus_Name(error_code);
msg += ", ";
}
msg += "(ERR:ftrace_parse)"; // special marker for UI
- return base::Status(msg);
+ if (error_count > 0) {
+ return base::Status(msg);
+ }
}
}
@@ -3292,5 +3305,4 @@
return name_id;
}
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc b/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
index 3cbdc80..129271c 100644
--- a/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
+++ b/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
@@ -69,6 +69,7 @@
PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::Mmap2Record rec;
+ rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
rec.filename = "file1";
rec.num.addr = 1000;
rec.num.len = 100;
@@ -94,6 +95,7 @@
PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
PerfDataTracker::Mmap2Record rec;
+ rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
rec.filename = "file1";
rec.num.addr = 1000;
rec.num.len = 100;
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index 2e542d0..03b0399 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -26,6 +26,8 @@
"chrome_system_probes_parser.h",
"default_modules.cc",
"default_modules.h",
+ "jit_tracker.cc",
+ "jit_tracker.h",
"memory_tracker_snapshot_module.cc",
"memory_tracker_snapshot_module.h",
"memory_tracker_snapshot_parser.cc",
@@ -244,6 +246,7 @@
sources = [
"active_chrome_processes_tracker_unittest.cc",
"heap_graph_tracker_unittest.cc",
+ "jit_tracker_unittest.cc",
"network_trace_module_unittest.cc",
"perf_sample_tracker_unittest.cc",
"profile_packet_sequence_state_unittest.cc",
@@ -272,7 +275,9 @@
"../../../protozero",
"../../sorter",
"../../storage",
+ "../../tables",
"../../types",
+ "../../util:build_id",
"../../util:descriptors",
"../../util:profiler_util",
"../common",
diff --git a/src/trace_processor/importers/proto/jit_tracker.cc b/src/trace_processor/importers/proto/jit_tracker.cc
new file mode 100644
index 0000000..96eddb0
--- /dev/null
+++ b/src/trace_processor/importers/proto/jit_tracker.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/jit_tracker.h"
+
+#include <cstddef>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/jit_cache.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+JitTracker::~JitTracker() = default;
+
+JitCache* JitTracker::CreateJitCache(std::string name,
+ UniquePid upid,
+ AddressRange range) {
+ auto cache =
+ std::make_unique<JitCache>(context_, std::move(name), upid, range);
+ JitCache* cache_ptr = cache.get();
+ // Dealing with overlaps is complicated. Do we delete the entire range, only
+ // the overlap, how do we deal with requests to the old JitCache. And it
+ // doesn't really happen in practice (e.g. for v8 you would need to delete an
+ // isolate and recreate it.), so just make sure our assumption (this never
+ // happens) is correct with a check.
+ PERFETTO_CHECK(caches_[upid].Emplace(range, std::move(cache)));
+ context_->mapping_tracker->AddJitRange(upid, range, cache_ptr);
+ return cache_ptr;
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/jit_tracker.h b/src/trace_processor/importers/proto/jit_tracker.h
new file mode 100644
index 0000000..e6eb5c8
--- /dev/null
+++ b/src/trace_processor/importers/proto/jit_tracker.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_JIT_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_JIT_TRACKER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class JitCache;
+
+// Keeps track of Jitted code.
+class JitTracker : public Destructible {
+ public:
+ static JitTracker* GetOrCreate(TraceProcessorContext* context) {
+ if (!context->jit_tracker) {
+ context->jit_tracker.reset(new JitTracker(context));
+ }
+ return static_cast<JitTracker*>(context->jit_tracker.get());
+ }
+
+ ~JitTracker() override;
+
+ // Creates a JitCache. Any frame interning request for the given pid in the
+ // given address range will be forwarded from the StackProfileTracker to this
+ // cache.
+ JitCache* CreateJitCache(std::string name,
+ UniquePid upid,
+ AddressRange range);
+
+ private:
+ explicit JitTracker(TraceProcessorContext* context) : context_(context) {}
+
+ FrameId InternUnknownFrame(MappingId mapping_id, uint64_t rel_pc);
+
+ TraceProcessorContext* const context_;
+
+ base::FlatHashMap<UniquePid, AddressRangeMap<std::unique_ptr<JitCache>>>
+ caches_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_JIT_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/jit_tracker_unittest.cc b/src/trace_processor/importers/proto/jit_tracker_unittest.cc
new file mode 100644
index 0000000..1903340
--- /dev/null
+++ b/src/trace_processor/importers/proto/jit_tracker_unittest.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/jit_tracker.h"
+
+#include <cstdint>
+#include <optional>
+#include <string>
+
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/jit_cache.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/jit_tables_py.h"
+#include "src/trace_processor/util/build_id.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::FieldsAre;
+using ::testing::IsEmpty;
+using ::testing::Ne;
+using ::testing::Optional;
+using ::testing::SaveArg;
+
+class JitTrackerTest : public testing::Test {
+ public:
+ JitTrackerTest() {
+ context_.storage.reset(new TraceStorage());
+ context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
+ context_.mapping_tracker.reset(new MappingTracker(&context_));
+ context_.process_tracker.reset(new ProcessTracker(&context_));
+ jit_tracker_ = JitTracker::GetOrCreate(&context_);
+ }
+
+ protected:
+ UserMemoryMapping& AddMapping(UniquePid upid,
+ AddressRange range,
+ uint64_t exact_offset = 0,
+ uint64_t load_bias = 0) {
+ uint32_t id = context_.storage->stack_profile_mapping_table().row_count();
+ CreateMappingParams params;
+ params.memory_range = range;
+ params.build_id =
+ BuildId::FromRaw(reinterpret_cast<const uint8_t*>(&id), sizeof(id));
+ params.exact_offset = exact_offset;
+ params.start_offset = exact_offset;
+ params.load_bias = load_bias;
+ params.name = "Mapping ";
+ params.name += std::to_string(id);
+ return context_.mapping_tracker->CreateUserMemoryMapping(upid,
+ std::move(params));
+ }
+
+ TraceProcessorContext context_;
+ JitTracker* jit_tracker_;
+};
+
+TEST_F(JitTrackerTest, BasicFunctionality) {
+ const UniquePid upid = context_.process_tracker->GetOrCreateProcess(1234);
+ const UniqueTid utid = context_.process_tracker->UpdateThread(4321, 1234);
+ const AddressRange jit_range(0, 1000);
+ auto& mapping = AddMapping(upid, jit_range);
+ JitCache* cache = jit_tracker_->CreateJitCache("name", upid, jit_range);
+
+ const StringId function_name = context_.storage->InternString("Function 1");
+ const StringId source_file = context_.storage->InternString("SourceFile");
+ const int64_t create_ts = 12345;
+ const AddressRange code_range(0, 100);
+
+ auto code_id = cache->LoadCode(create_ts, utid, code_range, function_name,
+ JitCache::SourceLocation{source_file, 10},
+ TraceBlobView());
+
+ auto code = *context_.storage->jit_code_table().FindById(code_id);
+ EXPECT_THAT(code.create_ts(), Eq(create_ts));
+ EXPECT_THAT(code.estimated_delete_ts(), Eq(std::nullopt));
+ EXPECT_THAT(code.utid(), Eq(utid));
+ EXPECT_THAT(code.start_address(),
+ Eq(static_cast<int64_t>(code_range.start())));
+ EXPECT_THAT(code.size(), Eq(static_cast<int64_t>(code_range.size())));
+ EXPECT_THAT(code.function_name(), Eq(function_name));
+
+ auto frame_id = mapping.InternFrame(50, "");
+
+ auto frame =
+ *context_.storage->stack_profile_frame_table().FindById(frame_id);
+ EXPECT_THAT(frame.name(), Eq(function_name));
+
+ auto row = context_.storage->jit_frame_table().FindById(
+ tables::JitFrameTable::Id(0));
+ ASSERT_THAT(row, Ne(std::nullopt));
+
+ EXPECT_THAT(row->jit_code_id(), Eq(code_id));
+ EXPECT_THAT(row->frame_id(), Eq(frame_id));
+}
+
+TEST_F(JitTrackerTest, FunctionOverlapUpdatesDeleteTs) {
+ const UniquePid upid = context_.process_tracker->GetOrCreateProcess(1234);
+ const UniqueTid utid = context_.process_tracker->UpdateThread(4321, 1234);
+ const AddressRange jit_range(0, 1000);
+ auto& mapping = AddMapping(upid, jit_range);
+ JitCache* cache = jit_tracker_->CreateJitCache("name", upid, jit_range);
+
+ const StringId function_name_1 = context_.storage->InternString("Function 1");
+ const StringId function_name_2 = context_.storage->InternString("Function 2");
+ const StringId source_file = context_.storage->InternString("SourceFile");
+ const int64_t create_ts_1 = 12345;
+ const int64_t create_ts_2 = 23456;
+ const AddressRange code_range_1(0, 100);
+ const AddressRange code_range_2(50, 200);
+
+ auto code_id_1 = cache->LoadCode(
+ create_ts_1, utid, code_range_1, function_name_1,
+ JitCache::SourceLocation{source_file, 10}, TraceBlobView());
+ auto code_id_2 = cache->LoadCode(
+ create_ts_2, utid, code_range_2, function_name_2,
+ JitCache::SourceLocation{source_file, 10}, TraceBlobView());
+ EXPECT_THAT(code_id_1, Ne(code_id_2));
+
+ auto code_1 = *context_.storage->jit_code_table().FindById(code_id_1);
+ auto code_2 = *context_.storage->jit_code_table().FindById(code_id_2);
+
+ // Code 1 has been deleted
+ EXPECT_THAT(code_1.create_ts(), Eq(create_ts_1));
+ EXPECT_THAT(code_1.estimated_delete_ts(), Eq(create_ts_2));
+
+ // The only active code is 2 at this point.
+ EXPECT_THAT(code_2.create_ts(), Eq(create_ts_2));
+ EXPECT_THAT(code_2.estimated_delete_ts(), Eq(std::nullopt));
+
+ // No frame should mention code 1
+ FrameId frame_id = mapping.InternFrame(50, "");
+ auto frame_a =
+ *context_.storage->stack_profile_frame_table().FindById(frame_id);
+ EXPECT_THAT(frame_a.name(), Eq(function_name_2));
+ ASSERT_THAT(context_.storage->jit_frame_table().row_count(), Eq(1u));
+ auto row = context_.storage->jit_frame_table().FindById(
+ tables::JitFrameTable::Id(0));
+ EXPECT_THAT(row->jit_code_id(), Eq(code_id_2));
+ EXPECT_THAT(row->frame_id(), Eq(frame_id));
+
+ // Frames for the old code 1 must fail to resolve to a jitted function but
+ // still generate a frame.
+ EXPECT_THAT(context_.storage->stats().at(stats::jit_unknown_frame).value,
+ Eq(0));
+ frame_id = mapping.InternFrame(0, "custom");
+ EXPECT_THAT(context_.storage->stats().at(stats::jit_unknown_frame).value,
+ Eq(1));
+ auto frame_b =
+ *context_.storage->stack_profile_frame_table().FindById(frame_id);
+ EXPECT_THAT(frame_a.id(), Ne(frame_b.id()));
+ EXPECT_THAT(context_.storage->GetString(frame_b.name()), Eq("custom"));
+ EXPECT_THAT(context_.storage->jit_frame_table().row_count(), Eq(1u));
+}
+
+} // namespace
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/packet_sequence_state_generation.h b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
index 1f05d5c..f310309 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state_generation.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
@@ -84,6 +84,8 @@
return generation_->GetOrCreate<T>();
}
+ PacketSequenceState* state() const { return generation_->state(); }
+
private:
friend PacketSequenceStateGeneration;
// Called when the a new generation is created as a result of
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index b80cae7..f8e2711 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -158,7 +158,8 @@
uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
- UniqueTid utid = procs->UpdateThread(tid, pid);
+ const UniqueTid utid = procs->UpdateThread(tid, pid);
+ const UniquePid upid = procs->GetOrCreateProcess(pid);
// Iterate through timestamps and callstacks simultaneously.
auto timestamp_it = packet.timestamp_delta_us();
@@ -172,7 +173,7 @@
}
auto opt_cs_id =
- stack_profile_sequence_state.FindOrInsertCallstack(*callstack_it);
+ stack_profile_sequence_state.FindOrInsertCallstack(upid, *callstack_it);
if (!opt_cs_id) {
context_->storage->IncrementStats(stats::stackprofile_parser_error);
continue;
@@ -247,11 +248,16 @@
ts, static_cast<double>(sample.timebase_count()),
sampling_stream.timebase_track_id);
+ const UniqueTid utid =
+ context_->process_tracker->UpdateThread(sample.tid(), sample.pid());
+ const UniquePid upid =
+ context_->process_tracker->GetOrCreateProcess(sample.pid());
+
StackProfileSequenceState& stack_profile_sequence_state =
*sequence_state->GetOrCreate<StackProfileSequenceState>();
uint64_t callstack_iid = sample.callstack_iid();
std::optional<CallsiteId> cs_id =
- stack_profile_sequence_state.FindOrInsertCallstack(callstack_iid);
+ stack_profile_sequence_state.FindOrInsertCallstack(upid, callstack_iid);
// A failed lookup of the interned callstack can mean either:
// (a) This is a counter-only profile without callstacks. Due to an
@@ -274,9 +280,6 @@
return;
}
- UniqueTid utid =
- context_->process_tracker->UpdateThread(sample.tid(), sample.pid());
-
using protos::pbzero::Profiling;
TraceStorage* storage = context_->storage.get();
diff --git a/src/trace_processor/importers/proto/profile_packet_sequence_state.cc b/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
index 31841fc..d909768 100644
--- a/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state.cc
@@ -103,8 +103,10 @@
void ProfilePacketSequenceState::AddFrame(SourceFrameId id,
const SourceFrame& frame) {
- VirtualMemoryMapping** mapping = mappings_.Find(frame.mapping_id);
- if (!mapping) {
+ VirtualMemoryMapping* mapping;
+ if (auto* ptr = mappings_.Find(frame.mapping_id); ptr) {
+ mapping = *ptr;
+ } else {
context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
return;
}
@@ -116,7 +118,8 @@
}
FrameId frame_id =
- (*mapping)->InternFrame(frame.rel_pc, base::StringView(*function_name));
+ mapping->InternFrame(frame.rel_pc, base::StringView(*function_name));
+ PERFETTO_CHECK(!mapping->is_jitted());
frames_.Insert(id, frame_id);
}
@@ -174,15 +177,14 @@
}
void ProfilePacketSequenceState::AddAllocation(const SourceAllocation& alloc) {
- auto opt_callstack_id = FindOrInsertCallstack(alloc.callstack_id);
+ const UniquePid upid = context_->process_tracker->GetOrCreateProcess(
+ static_cast<uint32_t>(alloc.pid));
+ auto opt_callstack_id = FindOrInsertCallstack(upid, alloc.callstack_id);
if (!opt_callstack_id)
return;
CallsiteId callstack_id = *opt_callstack_id;
- UniquePid upid = context_->process_tracker->GetOrCreateProcess(
- static_cast<uint32_t>(alloc.pid));
-
tables::HeapProfileAllocationTable::Row alloc_row{
alloc.timestamp,
upid,
@@ -277,11 +279,13 @@
}
std::optional<CallsiteId> ProfilePacketSequenceState::FindOrInsertCallstack(
+ UniquePid upid,
uint64_t iid) {
if (CallsiteId* id = callstacks_.Find(iid); id) {
return *id;
}
- return GetOrCreate<StackProfileSequenceState>()->FindOrInsertCallstack(iid);
+ return GetOrCreate<StackProfileSequenceState>()->FindOrInsertCallstack(upid,
+ iid);
}
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/profile_packet_sequence_state.h b/src/trace_processor/importers/proto/profile_packet_sequence_state.h
index 99661da..3fb8dcd 100644
--- a/src/trace_processor/importers/proto/profile_packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/profile_packet_sequence_state.h
@@ -122,7 +122,7 @@
// via the Add* methods), and then, if this lookup fails, in the InternedData
// instead.
std::optional<MappingId> FindOrInsertMapping(uint64_t iid);
- std::optional<CallsiteId> FindOrInsertCallstack(uint64_t iid);
+ std::optional<CallsiteId> FindOrInsertCallstack(UniquePid upid, uint64_t iid);
TraceProcessorContext* const context_;
diff --git a/src/trace_processor/importers/proto/stack_profile_sequence_state.cc b/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
index a469f92..2688d26 100644
--- a/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
+++ b/src/trace_processor/importers/proto/stack_profile_sequence_state.cc
@@ -16,17 +16,20 @@
#include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
+#include <cstdint>
#include <optional>
+#include <utility>
#include <vector>
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
#include "src/trace_processor/importers/common/address_range.h"
#include "src/trace_processor/importers/common/mapping_tracker.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/stack_profile_tracker.h"
-#include "src/trace_processor/importers/proto/packet_sequence_state.h"
#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
#include "src/trace_processor/importers/proto/profile_packet_utils.h"
#include "src/trace_processor/storage/stats.h"
@@ -41,6 +44,15 @@
return base::StringView(reinterpret_cast<const char*>(bytes.data),
bytes.size);
}
+
+// Determine wether this is the magical kernel mapping created in
+// `perfetto::::profiling::Unwinder::SymbolizeKernelCallchain`
+bool IsMagicalKernelMapping(const CreateMappingParams& params) {
+ return params.memory_range.start() == 0 &&
+ params.memory_range.length() == 0 && params.exact_offset == 0 &&
+ !params.build_id.has_value() && (params.name == "/kernel");
+}
+
} // namespace
StackProfileSequenceState::StackProfileSequenceState(
@@ -49,17 +61,22 @@
StackProfileSequenceState::~StackProfileSequenceState() = default;
-std::optional<MappingId> StackProfileSequenceState::FindOrInsertMapping(
+VirtualMemoryMapping* StackProfileSequenceState::FindOrInsertMapping(
uint64_t iid) {
- if (VirtualMemoryMapping* mapping = FindOrInsertMappingImpl(iid); mapping) {
- return mapping->mapping_id();
+ if (state()->pid_and_tid_valid()) {
+ return FindOrInsertMappingImpl(
+ context_->process_tracker->GetOrCreateProcess(
+ static_cast<uint32_t>(state()->pid())),
+ iid);
}
- return std::nullopt;
+
+ return FindOrInsertMappingImpl(std::nullopt, iid);
}
VirtualMemoryMapping* StackProfileSequenceState::FindOrInsertMappingImpl(
+ std::optional<UniquePid> upid,
uint64_t iid) {
- if (auto ptr = cached_mappings_.Find(iid); ptr) {
+ if (auto ptr = cached_mappings_.Find({upid, iid}); ptr) {
return *ptr;
}
auto* decoder =
@@ -87,7 +104,9 @@
if (!build_id) {
return nullptr;
}
- params.build_id = BuildId::FromRaw(*build_id);
+ if (!build_id->empty()) {
+ params.build_id = BuildId::FromRaw(*build_id);
+ }
params.memory_range = AddressRange(decoder->start(), decoder->end());
params.exact_offset = decoder->exact_offset();
@@ -95,11 +114,26 @@
params.load_bias = decoder->load_bias();
params.name = ProfilePacketUtils::MakeMappingName(path_components);
- VirtualMemoryMapping& mapping =
- context_->mapping_tracker->InternMemoryMapping(std::move(params));
+ VirtualMemoryMapping* mapping;
- cached_mappings_.Insert(iid, &mapping);
- return &mapping;
+ if (IsMagicalKernelMapping(params)) {
+ mapping = &context_->mapping_tracker->CreateKernelMemoryMapping(
+ std::move(params));
+ // A lot of tests to not set a proper mapping range
+ // Dummy mappings can also be emitted (e.g. for errors during unwinding)
+ } else if (params.memory_range.empty()) {
+ mapping =
+ &context_->mapping_tracker->InternMemoryMapping(std::move(params));
+ } else if (upid.has_value()) {
+ mapping = &context_->mapping_tracker->CreateUserMemoryMapping(
+ *upid, std::move(params));
+ } else {
+ mapping =
+ &context_->mapping_tracker->InternMemoryMapping(std::move(params));
+ }
+
+ cached_mappings_.Insert({upid, iid}, mapping);
+ return mapping;
}
std::optional<base::StringView>
@@ -134,8 +168,9 @@
}
std::optional<CallsiteId> StackProfileSequenceState::FindOrInsertCallstack(
+ UniquePid upid,
uint64_t iid) {
- if (CallsiteId* id = cached_callstacks_.Find(iid); id) {
+ if (CallsiteId* id = cached_callstacks_.Find({upid, iid}); id) {
return *id;
}
auto* decoder = LookupInternedMessage<
@@ -149,7 +184,7 @@
std::optional<CallsiteId> parent_callsite_id;
uint32_t depth = 0;
for (auto it = decoder->frame_ids(); it; ++it) {
- std::optional<FrameId> frame_id = FindOrInsertFrame(*it);
+ std::optional<FrameId> frame_id = FindOrInsertFrame(upid, *it);
if (!frame_id) {
return std::nullopt;
}
@@ -163,14 +198,15 @@
return std::nullopt;
}
- cached_callstacks_.Insert(iid, *parent_callsite_id);
+ cached_callstacks_.Insert({upid, iid}, *parent_callsite_id);
return parent_callsite_id;
}
std::optional<FrameId> StackProfileSequenceState::FindOrInsertFrame(
+ UniquePid upid,
uint64_t iid) {
- if (FrameId* id = cached_frames_.Find(iid); id) {
+ if (FrameId* id = cached_frames_.Find({upid, iid}); id) {
return *id;
}
auto* decoder =
@@ -182,7 +218,7 @@
}
VirtualMemoryMapping* mapping =
- FindOrInsertMappingImpl(decoder->mapping_id());
+ FindOrInsertMappingImpl(upid, decoder->mapping_id());
if (!mapping) {
return std::nullopt;
}
@@ -198,7 +234,9 @@
}
FrameId frame_id = mapping->InternFrame(decoder->rel_pc(), function_name);
- cached_frames_.Insert(iid, frame_id);
+ if (!mapping->is_jitted()) {
+ cached_frames_.Insert({upid, iid}, frame_id);
+ }
return frame_id;
}
diff --git a/src/trace_processor/importers/proto/stack_profile_sequence_state.h b/src/trace_processor/importers/proto/stack_profile_sequence_state.h
index 82de785..518388d 100644
--- a/src/trace_processor/importers/proto/stack_profile_sequence_state.h
+++ b/src/trace_processor/importers/proto/stack_profile_sequence_state.h
@@ -21,7 +21,9 @@
#include <optional>
#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
@@ -41,21 +43,64 @@
virtual ~StackProfileSequenceState() override;
- std::optional<MappingId> FindOrInsertMapping(uint64_t iid);
- std::optional<CallsiteId> FindOrInsertCallstack(uint64_t iid);
+ // Returns `nullptr`if non could be found.
+ VirtualMemoryMapping* FindOrInsertMapping(uint64_t iid);
+ std::optional<CallsiteId> FindOrInsertCallstack(UniquePid upid, uint64_t iid);
private:
- // Returns `nullptr`if non could be found.
- VirtualMemoryMapping* FindOrInsertMappingImpl(uint64_t iid);
std::optional<base::StringView> LookupInternedBuildId(uint64_t iid);
std::optional<base::StringView> LookupInternedMappingPath(uint64_t iid);
std::optional<base::StringView> LookupInternedFunctionName(uint64_t iid);
- std::optional<FrameId> FindOrInsertFrame(uint64_t iid);
+
+ // Returns `nullptr`if non could be found.
+ VirtualMemoryMapping* FindOrInsertMappingImpl(std::optional<UniquePid> upid,
+ uint64_t iid);
+ std::optional<FrameId> FindOrInsertFrame(UniquePid upid, uint64_t iid);
TraceProcessorContext* const context_;
- base::FlatHashMap<uint64_t, VirtualMemoryMapping*> cached_mappings_;
- base::FlatHashMap<uint64_t, CallsiteId> cached_callstacks_;
- base::FlatHashMap<uint64_t, FrameId> cached_frames_;
+
+ struct OptionalUniquePidAndIid {
+ std::optional<UniquePid> upid;
+ uint64_t iid;
+
+ bool operator==(const OptionalUniquePidAndIid& o) const {
+ return upid == o.upid && iid == o.iid;
+ }
+
+ struct Hasher {
+ size_t operator()(const OptionalUniquePidAndIid& o) const {
+ base::Hasher h;
+ h.Update(o.iid);
+ if (o.upid) {
+ h.Update(*o.upid);
+ }
+ return static_cast<size_t>(h.digest());
+ }
+ };
+ };
+
+ struct UniquePidAndIid {
+ UniquePid upid;
+ uint64_t iid;
+
+ bool operator==(const UniquePidAndIid& o) const {
+ return upid == o.upid && iid == o.iid;
+ }
+
+ struct Hasher {
+ size_t operator()(const UniquePidAndIid& o) const {
+ return static_cast<size_t>(base::Hasher::Combine(o.upid, o.iid));
+ }
+ };
+ };
+ base::FlatHashMap<OptionalUniquePidAndIid,
+ VirtualMemoryMapping*,
+ OptionalUniquePidAndIid::Hasher>
+ cached_mappings_;
+ base::FlatHashMap<UniquePidAndIid, FrameId, UniquePidAndIid::Hasher>
+ cached_frames_;
+ base::FlatHashMap<UniquePidAndIid, CallsiteId, UniquePidAndIid::Hasher>
+ cached_callstacks_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 00bf52e..cdf2c45 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -196,14 +196,15 @@
}
// Interned mapping_id loses it's meaning when the sequence ends. So we need
// to get an id from stack_profile_mapping table.
- auto mapping_id = delegate.seq_state()
- ->GetOrCreate<StackProfileSequenceState>()
- ->FindOrInsertMapping(decoder->mapping_id());
- if (!mapping_id) {
+ auto mapping = delegate.seq_state()
+ ->GetOrCreate<StackProfileSequenceState>()
+ ->FindOrInsertMapping(decoder->mapping_id());
+ if (!mapping) {
return std::nullopt;
}
delegate.AddUnsignedInteger(
- util::ProtoToArgsParser::Key(prefix + ".mapping_id"), mapping_id->value);
+ util::ProtoToArgsParser::Key(prefix + ".mapping_id"),
+ mapping->mapping_id().value);
delegate.AddUnsignedInteger(util::ProtoToArgsParser::Key(prefix + ".rel_pc"),
decoder->rel_pc());
return base::OkStatus();
diff --git a/src/trace_processor/importers/proto/v8_tracker.h b/src/trace_processor/importers/proto/v8_tracker.h
index 6da4d12..3d85377 100644
--- a/src/trace_processor/importers/proto/v8_tracker.h
+++ b/src/trace_processor/importers/proto/v8_tracker.h
@@ -111,7 +111,7 @@
size_t operator()(const tables::V8JsFunctionTable::Row& v) const {
return static_cast<size_t>(base::Hasher::Combine(
v.name.raw_id(), v.v8_js_script_id.value, v.is_toplevel,
- v.kind.raw_id(), v.line.value_or(0), v.column.value_or(0)));
+ v.kind.raw_id(), v.line.value_or(0), v.col.value_or(0)));
}
};
base::FlatHashMap<tables::V8JsFunctionTable::Row,
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker.cc b/src/trace_processor/importers/syscalls/syscall_tracker.cc
index ed71317..4560d5f 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker.cc
+++ b/src/trace_processor/importers/syscalls/syscall_tracker.cc
@@ -24,8 +24,7 @@
#include "src/kernel_utils/syscall_table.h"
#include "src/trace_processor/storage/stats.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
// TODO(primiano): The current design is broken in case of 32-bit processes
// running on 64-bit kernel. At least on ARM, the syscal numbers don't match
@@ -48,8 +47,11 @@
const char* name = syscalls.GetById(i);
if (name && *name) {
id = context_->storage->InternString(name);
- if (!strcmp(name, "sys_write"))
+ if (!strcmp(name, "sys_write")) {
sys_write_string_id_ = id;
+ } else if (!strcmp(name, "sys_rt_sigreturn")) {
+ sys_rt_sigreturn_string_id_ = id;
+ }
} else {
base::StackString<64> unknown_str("sys_%zu", i);
id = context_->storage->InternString(unknown_str.string_view());
@@ -58,5 +60,4 @@
}
}
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker.h b/src/trace_processor/importers/syscalls/syscall_tracker.h
index 218256b..0005084 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker.h
+++ b/src/trace_processor/importers/syscalls/syscall_tracker.h
@@ -30,8 +30,7 @@
#include "src/trace_processor/types/destructible.h"
#include "src/trace_processor/types/trace_processor_context.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
class SyscallTracker : public Destructible {
public:
@@ -57,8 +56,16 @@
return;
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
- context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */, name,
- args_callback);
+
+ // sys_rt_sigreturn does not return so should be inserted as an instant
+ // event. See https://github.com/google/perfetto/issues/733 for details.
+ if (name == sys_rt_sigreturn_string_id_) {
+ context_->slice_tracker->Scoped(ts, track_id, kNullStringId, name, 0,
+ args_callback);
+ } else {
+ context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */,
+ name, args_callback);
+ }
if (name == sys_write_string_id_) {
if (utid >= in_sys_write_.size())
@@ -124,11 +131,11 @@
// the relevant StringId (this avoids having to always do two conversions).
std::array<StringId, kMaxSyscalls> arch_syscall_to_string_id_{};
StringId sys_write_string_id_ = std::numeric_limits<StringId>::max();
+ StringId sys_rt_sigreturn_string_id_ = std::numeric_limits<StringId>::max();
// UniqueTids currently in a sys_write syscall.
BitVector in_sys_write_;
};
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
#endif // SRC_TRACE_PROCESSOR_IMPORTERS_SYSCALLS_SYSCALL_TRACKER_H_
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
index f424a75..260d60d 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
+++ b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
@@ -49,6 +49,15 @@
StringId name,
SetArgsCallback args_callback),
(override));
+ MOCK_METHOD(std::optional<SliceId>,
+ Scoped,
+ (int64_t timestamp,
+ TrackId track_id,
+ StringId cat,
+ StringId name,
+ int64_t duration,
+ SetArgsCallback args_callback),
+ (override));
};
class SyscallTrackerTest : public ::testing::Test {
@@ -83,6 +92,14 @@
EXPECT_EQ(context.storage->GetString(end_name), "sys_57");
}
+TEST_F(SyscallTrackerTest, ReportSysreturn) {
+ EXPECT_CALL(*slice_tracker, Scoped(_, _, _, _, _, _)).Times(1);
+
+ SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(&context);
+ syscall_tracker->SetArchitecture(Architecture::kArm64);
+ syscall_tracker->Enter(100 /*ts*/, 42 /*utid*/, 139);
+}
+
TEST_F(SyscallTrackerTest, Arm64) {
constexpr TrackId track{0u};
StringId begin_name = kNullStringId;
diff --git a/src/trace_processor/metrics/sql/experimental/frame_times.sql b/src/trace_processor/metrics/sql/experimental/frame_times.sql
index 80ad550..a3a73c5 100644
--- a/src/trace_processor/metrics/sql/experimental/frame_times.sql
+++ b/src/trace_processor/metrics/sql/experimental/frame_times.sql
@@ -52,20 +52,26 @@
DROP VIEW IF EXISTS InterestingSegments;
CREATE PERFETTO VIEW InterestingSegments AS
-SELECT -- 1) Gestures overlapping interactions.
- ts_ge AS ts,
- dur_ge AS dur
-FROM InteractionEventsJoinGestureEvents
-WHERE ts_ge IS NOT NULL
-GROUP BY ts_ge
-UNION ALL
-SELECT -- 2) Interactions without gestures.
- ts_ir AS ts,
- dur_ir AS dur
-FROM InteractionEventsJoinGestureEvents
-WHERE ts_ge IS NULL
-GROUP BY ts_ir
-HAVING COUNT(*) = 1;
+WITH pre_cast AS (
+ SELECT -- 1) Gestures overlapping interactions.
+ ts_ge AS ts,
+ dur_ge AS dur
+ FROM InteractionEventsJoinGestureEvents
+ WHERE ts_ge IS NOT NULL
+ GROUP BY ts_ge
+ UNION ALL
+ SELECT -- 2) Interactions without gestures.
+ ts_ir AS ts,
+ dur_ir AS dur
+ FROM InteractionEventsJoinGestureEvents
+ WHERE ts_ge IS NULL
+ GROUP BY ts_ir
+ HAVING COUNT(*) = 1
+)
+SELECT
+ CAST(ts AS BIGINT) AS ts,
+ CAST(dur AS BIGINT) AS dur
+FROM pre_cast;
--------------------------------------------------------------------------------
-- On ChromeOS, DRM events, if they exist, are the source of truth. Otherwise,
diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn
index e34973c..8082a5d 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn
@@ -18,6 +18,8 @@
source_set("functions") {
sources = [
+ "base64.cc",
+ "base64.h",
"clock_functions.h",
"create_function.cc",
"create_function.h",
diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc b/src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc
new file mode 100644
index 0000000..9c16baf
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/functions/base64.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/trace_processor/perfetto_sql/intrinsics/functions/base64.h"
+
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/base64.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/functions/sql_function.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto::trace_processor {
+
+namespace {
+
+struct Base64Decode : public SqlFunction {
+ static base::Status Run(Context*,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors& destructors) {
+ if (argc != 1) {
+ return base::ErrStatus("BASE64: expected one arg but got %zu", argc);
+ }
+
+ auto in = sqlite_utils::SqliteValueToSqlValue(argv[0]);
+
+ const char* src = nullptr;
+ size_t src_size = 0;
+ switch (in.type) {
+ case SqlValue::kNull:
+ return base::OkStatus();
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ return base::ErrStatus("BASE64: argument must be string or blob");
+ case SqlValue::kString:
+ src = in.AsString();
+ src_size = strlen(src);
+ break;
+ case SqlValue::kBytes:
+ src = reinterpret_cast<const char*>(in.AsBytes());
+ src_size = in.bytes_count;
+ break;
+ }
+
+ size_t dst_size = base::Base64DecSize(src_size);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(malloc(dst_size));
+ ssize_t res = base::Base64Decode(src, src_size, dst, dst_size);
+ if (res < 0) {
+ free(dst);
+ return base::ErrStatus("BASE64: Invalid input");
+ }
+ dst_size = static_cast<size_t>(res);
+ out = SqlValue::Bytes(dst, dst_size);
+ destructors.bytes_destructor = free;
+ return base::OkStatus();
+ }
+};
+
+} // namespace
+
+base::Status RegisterBase64Functions(PerfettoSqlEngine& engine) {
+ return engine.RegisterStaticFunction<Base64Decode>("base64_decode", 1,
+ nullptr, true);
+}
+
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/base64.h b/src/trace_processor/perfetto_sql/intrinsics/functions/base64.h
new file mode 100644
index 0000000..a7125a5
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/functions/base64.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_BASE64_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_BASE64_H_
+
+#include "perfetto/base/status.h"
+
+namespace perfetto::trace_processor {
+
+class PerfettoSqlEngine;
+
+base::Status RegisterBase64Functions(PerfettoSqlEngine& engine);
+
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_FUNCTIONS_BASE64_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 631942d..6edd50b 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
@@ -43,6 +43,8 @@
"experimental_slice_layout.h",
"flamegraph_construction_algorithms.cc",
"flamegraph_construction_algorithms.h",
+ "interval_intersect.cc",
+ "interval_intersect.h",
"table_info.cc",
"table_info.h",
]
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc
new file mode 100644
index 0000000..db83694
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.cc
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/protozero/proto_decoder.h"
+#include "perfetto/protozero/proto_utils.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/status.h"
+#include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/column.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/tables_py.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto::trace_processor {
+namespace tables {
+IntervalIntersectTable::~IntervalIntersectTable() = default;
+} // namespace tables
+
+namespace {
+
+using RepeatedDecoder = protos::pbzero::RepeatedBuilderResult::Decoder;
+using RepeatedIter = ::protozero::PackedRepeatedFieldIterator<
+ ::protozero::proto_utils::ProtoWireType::kFixed64,
+ int64_t>;
+
+base::StatusOr<RepeatedIter> DecodeArgument(const SqlValue& raw_arg,
+ const char* debug_name,
+ bool& parse_error) {
+ if (raw_arg.type != SqlValue::kBytes) {
+ return base::ErrStatus(
+ "interval_intersect: '%s' should be a repeated field", debug_name);
+ }
+ protos::pbzero::ProtoBuilderResult::Decoder proto_arg(
+ static_cast<const uint8_t*>(raw_arg.AsBytes()), raw_arg.bytes_count);
+ if (!proto_arg.is_repeated()) {
+ return base::ErrStatus(
+ "interval_intersect: '%s' is not generated by RepeatedField "
+ "function",
+ debug_name);
+ }
+
+ auto iter =
+ protos::pbzero::RepeatedBuilderResult::Decoder(proto_arg.repeated())
+ .int_values(&parse_error);
+ if (parse_error) {
+ return base::ErrStatus(
+ "interval_intersect: error when parsing '%s' values.", debug_name);
+ }
+
+ return iter;
+}
+
+struct Interval {
+ int64_t id;
+ int64_t ts;
+ int64_t dur;
+
+ int64_t end() { return ts + dur; }
+};
+
+struct IntervalsIterator {
+ RepeatedIter ids;
+ RepeatedIter tses;
+ RepeatedIter durs;
+
+ static base::StatusOr<IntervalsIterator> Create(
+ const SqlValue& raw_ids,
+ const SqlValue& raw_timestamps,
+ const SqlValue& raw_durs,
+ bool& parse_error) {
+ ASSIGN_OR_RETURN(RepeatedIter ids,
+ DecodeArgument(raw_ids, "ids", parse_error));
+ ASSIGN_OR_RETURN(RepeatedIter tses,
+ DecodeArgument(raw_timestamps, "timestamps", parse_error));
+ ASSIGN_OR_RETURN(RepeatedIter durs,
+ DecodeArgument(raw_durs, "durations", parse_error));
+
+ return IntervalsIterator{ids, tses, durs};
+ }
+
+ void operator++() {
+ PERFETTO_DCHECK(ids && tses && durs);
+ ids++;
+ tses++;
+ durs++;
+ }
+
+ Interval operator*() const { return Interval{*ids, *tses, *durs}; }
+
+ explicit operator bool() const { return bool(ids); }
+};
+
+} // namespace
+IntervalIntersect::IntervalIntersect(StringPool* pool) : pool_(pool) {}
+IntervalIntersect::~IntervalIntersect() = default;
+
+Table::Schema IntervalIntersect::CreateSchema() {
+ return tables::IntervalIntersectTable::ComputeStaticSchema();
+}
+
+std::string IntervalIntersect::TableName() {
+ return tables::IntervalIntersectTable::Name();
+}
+
+uint32_t IntervalIntersect::EstimateRowCount() {
+ // TODO(mayzner): Give proper estimate.
+ return 1024;
+}
+
+base::StatusOr<std::unique_ptr<Table>> IntervalIntersect::ComputeTable(
+ const std::vector<SqlValue>& arguments) {
+ PERFETTO_DCHECK(arguments.size() == 6);
+
+ bool parse_error = false;
+ ASSIGN_OR_RETURN(IntervalsIterator l_it,
+ IntervalsIterator::Create(arguments[0], arguments[1],
+ arguments[2], parse_error));
+ ASSIGN_OR_RETURN(IntervalsIterator r_it,
+ IntervalsIterator::Create(arguments[3], arguments[4],
+ arguments[5], parse_error));
+
+ // If there are no intervals in one of the tables then there are no intervals
+ // returned.
+ if (!l_it || !r_it) {
+ return std::unique_ptr<Table>(
+ std::make_unique<tables::IntervalIntersectTable>(pool_));
+ }
+
+ // We copy |l_it| and |r_it| for the second for loop.
+ IntervalsIterator l_it_2 = l_it;
+ IntervalsIterator r_it_2 = r_it;
+
+ auto table = std::make_unique<tables::IntervalIntersectTable>(pool_);
+
+ // Find all intersections where interval from right table started duringan
+ // interval from left table.
+ for (Interval l_i = *l_it; l_it && r_it && !parse_error;
+ ++l_it, l_i = *l_it) {
+ // If the next |r_i| starts after |l_i| ends, that means that we need to
+ // go the the next |l_i|, so we need to exit the loop.
+ for (Interval r_i = *r_it; r_it && r_i.ts < l_i.end() && !parse_error;
+ ++r_it, r_i = *r_it) {
+ // We already know (because we are in the loop) that |r_i| started before
+ // |l_i| ended, we should not intersect only if |r_i| started before
+ // |l_i|.
+ if (r_i.ts < l_i.ts) {
+ continue;
+ }
+
+ tables::IntervalIntersectTable::Row row;
+ row.ts = static_cast<uint32_t>(std::max(r_i.ts, l_i.ts));
+ row.dur = static_cast<uint32_t>(std::min(r_i.end(), l_i.end())) - row.ts;
+ row.left_id = static_cast<uint32_t>(l_i.id);
+ row.right_id = static_cast<uint32_t>(r_i.id);
+ table->Insert(row);
+ }
+ }
+
+ // Find all intersections where interval from the left table started during an
+ // interval from right table.
+ for (Interval r_i = *r_it_2; r_it_2 && l_it_2 && !parse_error;
+ ++r_it_2, r_i = *r_it_2) {
+ for (Interval l_i = *l_it_2; l_it_2 && l_i.ts < r_i.end() && !parse_error;
+ ++l_it_2, l_i = *l_it_2) {
+ // The only difference between this and above algorithm is not
+ // intersecting if the intervals started at the same time. We do this to
+ // prevent double counting intervals.
+ if (l_i.ts <= r_i.ts) {
+ continue;
+ }
+
+ tables::IntervalIntersectTable::Row row;
+ row.ts = static_cast<uint32_t>(std::max(r_i.ts, l_i.ts));
+ row.dur = static_cast<uint32_t>(std::min(r_i.end(), l_i.end())) - row.ts;
+ row.left_id = static_cast<uint32_t>(l_i.id);
+ row.right_id = static_cast<uint32_t>(r_i.id);
+ table->Insert(row);
+ }
+ }
+
+ if (parse_error) {
+ return base::ErrStatus(
+ "interval_intersect: Error in parsing of one of the arguments.");
+ }
+
+ return std::unique_ptr<Table>(std::move(table));
+}
+
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h
new file mode 100644
index 0000000..ec8ccdf
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/interval_intersect.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_INTERVAL_INTERSECT_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_INTERVAL_INTERSECT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
+
+namespace perfetto::trace_processor {
+
+// An SQL table-function which computes the intersection of intervals from two
+// tables.
+//
+// Given two sets of sorted non-overlapping intervals (with id, timestamp and
+// duration) returns intervals that are intersections between those two sets,
+// with ids to state what intervals are intersected.
+//
+// LEFT . - - - - - - .
+// RIGHT - - . - - . - -
+// Intersection . - . - - . - .
+//
+// Arguments are RepeatedBuilderResult protos containing a column of
+// numerics values:
+// 1) |in_left_ids|(uint32_t): Ids from the left table.
+// 2) |in_left_tses|(uint64_t): Timestamps (starts) of intervals from
+// the left table.
+// 3) |in_left_durs|(uint64_t): Durations of intervals
+// from the left table.
+// 4) |in_right_ids|(uint32_t): Ids from the right table.
+// 5) |in_right_tses|(uint64_t): Timestamps (starts) of intervals
+// from the right table.
+// 6) |in_right_durs|(uint64_t): Durations of intervals from the right table.
+//
+// NOTES:
+// - The first 3 arguments have to have the same number of values.
+// - Timestamps in left and right columns have to be sorted.
+//
+// Returns:
+// 1) |ts|: Start of the intersection.
+// 2) |dur|: Duration of the intersection.
+// 3) |left_id|: Id of the slice that was intersected in the first table.
+// 4) |right_id|: Id of the slice that was intersected in the second table.
+class IntervalIntersect : public StaticTableFunction {
+ public:
+ explicit IntervalIntersect(StringPool*);
+ virtual ~IntervalIntersect() override;
+
+ // StaticTableFunction implementation.
+ Table::Schema CreateSchema() override;
+ std::string TableName() override;
+ uint32_t EstimateRowCount() override;
+ base::StatusOr<std::unique_ptr<Table>> ComputeTable(
+ const std::vector<SqlValue>& arguments) override;
+
+ private:
+ StringPool* pool_;
+};
+
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_INTERVAL_INTERSECT_H_
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
index f71ab9c..3c86e50 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
@@ -170,6 +170,23 @@
flags=ColumnFlag.HIDDEN),
])
+INTERVAL_INTERSECT_TABLE = Table(
+ python_module=__file__,
+ class_name="IntervalIntersectTable",
+ sql_name="__intrinsic_interval_intersect",
+ columns=[
+ C("ts", CppInt64()),
+ C("dur", CppInt64()),
+ C("left_id", CppUint32()),
+ C("right_id", CppUint32()),
+ C("in_left_ids", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+ C("in_left_tses", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+ C("in_left_durs", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+ C("in_right_ids", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+ C("in_right_tses", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+ C("in_right_durs", CppOptional(CppString()), flags=ColumnFlag.HIDDEN),
+ ])
+
# Keep this list sorted.
ALL_TABLES = [
ANCESTOR_SLICE_BY_STACK_TABLE,
@@ -184,5 +201,6 @@
EXPERIMENTAL_COUNTER_DUR_TABLE,
EXPERIMENTAL_SCHED_UPID_TABLE,
EXPERIMENTAL_SLICE_LAYOUT_TABLE,
+ INTERVAL_INTERSECT_TABLE,
TABLE_INFO_TABLE,
]
diff --git a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
index c277966..69fa506 100644
--- a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
@@ -34,6 +34,7 @@
"sched",
"slices",
"time",
+ "v8",
]
generated_header = "stdlib.h"
namespace = "stdlib"
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
index bb4d758..89deaf0 100644
--- a/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
@@ -19,6 +19,7 @@
"args.sql",
"counters.sql",
"cpus.sql",
+ "jit.sql",
"metadata.sql",
"percentiles.sql",
"slices.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/jit.sql b/src/trace_processor/perfetto_sql/stdlib/common/jit.sql
new file mode 100644
index 0000000..4335a7e
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/common/jit.sql
@@ -0,0 +1,60 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+
+-- Represents a jitted code snippet.
+CREATE PERFETTO VIEW _jit_code (
+ -- Unique jit code id.
+ jit_code_id UINT,
+ -- Time this code was created / allocated.
+ create_ts LONG,
+ -- Time this code was destroyed / deallocated. This is a upper bound, as we
+ -- can only detect deletions indirectly when new code is allocated overlapping
+ -- existing one.
+ estimated_delete_ts LONG,
+ -- Thread that generated the code.
+ utid UINT,
+ -- Start address for the generated code.
+ start_address LONG,
+ -- Size in bytes of the generated code.
+ size LONG,
+ -- Function name.
+ function_name STRING,
+ -- Jitted code (binary data).
+ native_code BYTES
+) AS
+SELECT
+ id AS jit_code_id,
+ create_ts,
+ estimated_delete_ts,
+ utid,
+ start_address,
+ size,
+ function_name,
+ base64_decode(native_code_base64) AS native_code
+FROM __intrinsic_jit_code;
+
+-- Represents a jitted frame.
+CREATE PERFETTO VIEW _jit_frame (
+ -- Jitted code snipped the frame is in (joins with jit_code.jit_code_id).
+ jit_code_id UINT,
+ -- Jitted frame (joins with stack_profile_frame.id).
+ frame_id UINT
+) AS
+SELECT
+ jit_code_id,
+ frame_id
+FROM
+ __intrinsic_jit_frame;
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql
index 7590ffc..80b3c2e 100644
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql
@@ -14,6 +14,7 @@
-- limitations under the License.
INCLUDE PERFETTO MODULE deprecated.v42.common.timestamps;
+INCLUDE PERFETTO MODULE sched.time_in_state;
INCLUDE PERFETTO MODULE sched.states;
INCLUDE PERFETTO MODULE cpu.size;
@@ -55,34 +56,11 @@
-- The total duration.
dur INT
) AS
-WITH
-states_starting_inside AS (
- SELECT id
- FROM thread_state
- WHERE $ts <= ts
- AND ts <= $ts + $dur
- AND utid = $utid
-),
-first_state_starting_before AS (
- SELECT id
- FROM thread_state
- WHERE ts < $ts AND utid = $utid
- ORDER BY ts DESC
- LIMIT 1
-),
-relevant_states AS (
- SELECT * FROM states_starting_inside
- UNION ALL
- SELECT * FROM first_state_starting_before
-)
SELECT
sched_state_io_to_human_readable_string(state, io_wait) as state,
- state as raw_state,
+ state AS raw_state,
cpu_guess_core_type(cpu) as cpu_type,
cpu,
blocked_function,
- sum(spans_overlapping_dur($ts, $dur, ts, dur)) as dur
-FROM thread_state
-JOIN relevant_states USING (id)
-GROUP BY state, raw_state, cpu_type, cpu, blocked_function
-ORDER BY dur desc;
\ No newline at end of file
+ dur
+FROM sched_time_in_state_and_cpu_for_thread_in_interval($ts, $dur, $utid);
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn
index dcac571..e9e22e9 100644
--- a/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/intervals/BUILD.gn
@@ -15,5 +15,8 @@
import("../../../../../gn/perfetto_sql.gni")
perfetto_sql_source_set("intervals") {
- sources = [ "overlap.sql" ]
+ sources = [
+ "intersect.sql",
+ "overlap.sql",
+ ]
}
diff --git a/src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql b/src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql
new file mode 100644
index 0000000..2775e63
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql
@@ -0,0 +1,53 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+CREATE PERFETTO MACRO _interval_intersect(
+ left_table TableOrSubquery,
+ right_table TableOrSubquery
+)
+RETURNS TableOrSubquery AS
+(
+ WITH
+ __temp_left_table AS (SELECT * FROM $left_table ORDER BY ts),
+ __temp_right_table AS (SELECT * FROM $right_table ORDER BY ts)
+ SELECT ii.ts, ii.dur, ii.left_id, ii.right_id
+ FROM __intrinsic_interval_intersect(
+ (SELECT RepeatedField(id) FROM __temp_left_table),
+ (SELECT RepeatedField(ts) FROM __temp_left_table),
+ (SELECT RepeatedField(dur) FROM __temp_left_table),
+ (SELECT RepeatedField(id) FROM __temp_right_table),
+ (SELECT RepeatedField(ts) FROM __temp_right_table),
+ (SELECT RepeatedField(dur) FROM __temp_right_table)
+ ) ii
+);
+
+CREATE PERFETTO MACRO _interval_intersect_single(
+ ts Expr,
+ dur Expr,
+ intervals_table TableOrSubquery
+) RETURNS TableOrSubquery AS(
+ SELECT
+ left_id AS id,
+ ts,
+ dur
+ FROM _interval_intersect!(
+ $intervals_table,
+ (SELECT
+ 0 AS id,
+ $ts AS ts,
+ $dur AS dur
+ )
+ )
+)
diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql b/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
index 24f470a..6c58c60 100644
--- a/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
@@ -29,39 +29,4 @@
FROM slice ancestor
JOIN slice descendant
WHERE ancestor.id = $ancestor_id
- AND descendant.id = $descendant_id;
-
-CREATE PERFETTO MACRO _interval_intersect(
- left_table TableOrSubquery,
- right_table TableOrSubquery
-)
-RETURNS TableOrSubquery AS
-(
- WITH on_left AS (
- SELECT
- B.ts,
- IIF(
- A.ts + A.dur <= B.ts + B.dur,
- A.ts + A.dur - B.ts, B.dur) AS dur,
- A.id AS left_id,
- B.id as right_id
- FROM $left_table A
- JOIN $right_table B ON (A.ts <= B.ts AND A.ts + A.dur > B.ts)
- ), on_right AS (
- SELECT
- B.ts,
- IIF(
- A.ts + A.dur <= B.ts + B.dur,
- A.ts + A.dur - B.ts, B.dur) AS dur,
- B.id as left_id,
- A.id AS right_id
- FROM $right_table A
- -- The difference between this table and on_left is the lack of equality on
- -- A.ts <= B.ts. This is to remove the issue of double accounting
- -- timestamps that start at the same time.
- JOIN $left_table B ON (A.ts < B.ts AND A.ts + A.dur > B.ts)
- )
- SELECT * FROM on_left
- UNION ALL
- SELECT * FROM on_right
-);
\ No newline at end of file
+ AND descendant.id = $descendant_id;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql b/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql
index 6f9b3a3..8c6bc87 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql
@@ -13,8 +13,10 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
+INCLUDE PERFETTO MODULE intervals.intersect;
+
-- The time a thread spent in each scheduling state during it's lifetime.
-CREATE PERFETTO TABLE sched_thread_time_in_state(
+CREATE PERFETTO TABLE sched_time_in_state_for_thread(
-- Utid of the thread.
utid INT,
-- Total runtime of thread.
@@ -87,5 +89,80 @@
CASE WHEN state IN ('T', 't', 'X', 'Z', 'x', 'I', 'K', 'W', 'P', 'N')
THEN time_in_state END
) * 100/total_runtime AS other
-FROM sched_thread_time_in_state
-GROUP BY utid;
\ No newline at end of file
+FROM sched_time_in_state_for_thread
+GROUP BY utid;
+
+-- Time the thread spent each state in a given interval.
+CREATE PERFETTO FUNCTION sched_time_in_state_for_thread_in_interval(
+ -- The start of the interval.
+ ts INT,
+ -- The duration of the interval.
+ dur INT,
+ -- The utid of the thread.
+ utid INT)
+RETURNS TABLE(
+ -- Thread state (from the `thread_state` table).
+ -- Use `sched_state_to_human_readable_string` function to get full name.
+ state INT,
+ -- A (posssibly NULL) boolean indicating, if the device was in uninterruptible
+ -- sleep, if it was an IO sleep.
+ io_wait BOOL,
+ -- Some states can specify the blocked function. Usually NULL.
+ blocked_function INT,
+ -- Total time spent with this state, cpu and blocked function.
+ dur INT) AS
+SELECT
+ state,
+ io_wait,
+ blocked_function,
+ sum(ii.dur) as dur
+FROM thread_state
+JOIN
+ (SELECT * FROM _interval_intersect_single!(
+ $ts, $dur,
+ (SELECT id, ts, dur
+ FROM thread_state
+ WHERE utid = $utid))) ii USING (id)
+GROUP BY 1, 2, 3
+ORDER BY 4 DESC;
+
+-- Time the thread spent each state and cpu in a given interval.
+CREATE PERFETTO FUNCTION sched_time_in_state_and_cpu_for_thread_in_interval(
+ -- The start of the interval.
+ ts INT,
+ -- The duration of the interval.
+ dur INT,
+ -- The utid of the thread.
+ utid INT)
+RETURNS TABLE(
+ -- Thread state (from the `thread_state` table).
+ -- Use `sched_state_to_human_readable_string` function to get full name.
+ state INT,
+ -- A (posssibly NULL) boolean indicating, if the device was in uninterruptible
+ -- sleep, if it was an IO sleep.
+ io_wait BOOL,
+ -- Id of the CPU.
+ -- Use `cpu_guess_core_type` to get the CPU size (little/mid/big).
+ cpu INT,
+ -- Some states can specify the blocked function. Usually NULL.
+ blocked_function INT,
+ -- Total time spent with this state, cpu and blocked function.
+ dur INT) AS
+SELECT
+ state,
+ io_wait,
+ cpu,
+ blocked_function,
+ sum(ii.dur) as dur
+FROM thread_state
+JOIN
+ (SELECT * FROM _interval_intersect_single!(
+ $ts, $dur,
+ (SELECT id, ts, dur
+ FROM thread_state
+ WHERE utid = $utid))) ii USING (id)
+GROUP BY 1, 2, 3, 4
+ORDER BY 5 DESC;
+
+
+
diff --git a/src/trace_processor/perfetto_sql/stdlib/v8/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/v8/BUILD.gn
new file mode 100644
index 0000000..8bb2833
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/v8/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../../gn/perfetto_sql.gni")
+
+perfetto_sql_source_set("v8") {
+ sources = [ "jit.sql" ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/v8/jit.sql b/src/trace_processor/perfetto_sql/stdlib/v8/jit.sql
new file mode 100644
index 0000000..6765272
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/v8/jit.sql
@@ -0,0 +1,151 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- These are the tables for the V8 jit data source
+-- (protos/perfetto/trace/chrome/v8.proto).
+--
+-- All events are associated to a V8 isolate instance. There can be multiple
+-- instances associated to a given thread, although this is rare.
+--
+-- Generated code in V8 is allocated in the V8 heap (in a special executeable
+-- section), this means that code can be garbage collected (when no longer used)
+-- or can be moved around (e.g. during heap compactation). This means that a
+-- given callsite might correspond to function `A` at one point in time and to
+-- function `B` later on.
+-- In addition V8 code has various levels of optimization, so a function might
+-- have multiple associated code snippets.
+--
+-- V8 does not track code deletion, so we have to indirectly infer it by
+-- detecting code overlaps, if a newer code creation event overlaps with older
+-- code we need to asume that the old code was deleted. Code moves are logged,
+-- and there is an event to track those.
+
+-- A V8 Isolate instance. A V8 Isolate represents an isolated instance of the V8
+-- engine.
+CREATE PERFETTO VIEW v8_isolate(
+ -- Unique V8 isolate id.
+ v8_isolate_id UINT,
+ -- Process the isolate was created in.
+ upid UINT,
+ -- Internal id used by the v8 engine. Unique in a process.
+ internal_isolate_id UINT,
+ -- Absolute start address of the embedded code blob.
+ embedded_blob_code_start_address LONG,
+ -- Size in bytes of the embedded code blob.
+ embedded_blob_code_size LONG,
+ -- Base address of the code range if the isolate defines one.
+ code_range_base_address LONG,
+ -- Size of a code range if the isolate defines one.
+ code_range_size LONG,
+ -- Whether the code range for this Isolate is shared with others in the same
+ -- process. There is at max one such shared code range per process.
+ shared_code_range LONG,
+ -- Used when short builtin calls are enabled, where embedded builtins are
+ -- copied into the CodeRange so calls can be nearer.
+ embedded_blob_code_copy_start_address LONG
+) AS
+SELECT
+ id AS v8_isolate_id,
+ upid,
+ internal_isolate_id,
+ embedded_blob_code_start_address,
+ embedded_blob_code_size,
+ code_range_base_address,
+ code_range_size,
+ shared_code_range,
+ embedded_blob_code_copy_start_address
+FROM
+ __intrinsic_v8_isolate;
+
+
+-- Represents a script that was compiled to generate code. Some V8 code is
+-- generated out of scripts and will reference a V8Script other types of code
+-- will not (e.g. builtins).
+CREATE PERFETTO VIEW v8_js_script (
+ -- Unique V8 JS script id.
+ v8_js_script_id UINT,
+ -- V8 isolate this script belongs to (joinable with v8_isolate.v8_isolate_id).
+ v8_isolate_id UINT,
+ -- Script id used by the V8 engine.
+ internal_script_id UINT,
+ -- Script type.
+ script_type STRING,
+ -- Script name.
+ name STRING,
+ -- Actual contents of the script.
+ source STRING
+) AS
+SELECT
+ id AS v8_js_script_id,
+ v8_isolate_id,
+ internal_script_id,
+ script_type,
+ name,
+ source
+FROM
+ __intrinsic_v8_js_script;
+
+
+-- Represents one WASM script.
+CREATE PERFETTO VIEW v8_wasm_script (
+ -- Unique V8 WASM script id.
+ v8_wasm_script_id UINT,
+ -- V8 Isolate this script belongs to (joinable with v8_isolate.v8_isolate_id).
+ v8_isolate_id UINT,
+ -- Script id used by the V8 engine.
+ internal_script_id UINT,
+ -- URL of the source.
+ url STRING,
+ -- Actual contents of the script.
+ source STRING
+) AS
+SELECT
+ id AS v8_wasm_script_id,
+ v8_isolate_id,
+ internal_script_id,
+ url,
+ source
+FROM
+ __intrinsic_v8_wasm_script;
+
+
+-- Represents a v8 Javascript function.
+CREATE PERFETTO VIEW v8_js_function (
+ -- Unique V8 JS function id.
+ v8_js_function_id UINT,
+ -- Function name.
+ name STRING,
+ -- Script where the function is defined (joinable with
+ -- v8_js_script.v8_js_script_id).
+ v8_js_script_id UINT,
+ -- Whether this function represents the top level script.
+ is_toplevel BOOL,
+ -- Function kind (e.g. regular function or constructor).
+ kind STRING,
+ -- Line in script where function is defined. Starts at 1.
+ line UINT,
+ -- Column in script where function is defined. Starts at 1.
+ col UINT
+) AS
+SELECT
+ id AS v8_js_function_id,
+ name,
+ v8_js_script_id,
+ is_toplevel,
+ kind,
+ line,
+ col
+FROM
+ __intrinsic_v8_js_function;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 7fb6ce0..ae6f683 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -72,6 +72,8 @@
F(ftrace_setup_errors, kSingle, kInfo, kTrace, \
"One or more atrace/ftrace categories were not found or failed to " \
"enable. See ftrace_setup_errors in the metadata table for details."), \
+ F(ftrace_abi_errors_skipped_zero_data_length, \
+ kSingle, kInfo, kAnalysis, ""), \
F(fuchsia_non_numeric_counters, kSingle, kError, kAnalysis, ""), \
F(fuchsia_timestamp_overflow, kSingle, kError, kAnalysis, ""), \
F(fuchsia_invalid_event, kSingle, kError, kAnalysis, ""), \
@@ -310,6 +312,9 @@
F(winscope_protolog_missing_interned_stacktrace_parse_errors, \
kSingle, kInfo, kAnalysis, \
"Failed to find interned ProtoLog stacktrace."), \
+ F(jit_unknown_frame, kSingle, kDataLoss, kTrace, \
+ "Indicates that we were unable to determine the function for a frame in "\
+ "a jitted memory region"), \
F(ftrace_missing_event_id, kSingle, kInfo, kAnalysis, \
"Indicates that the ftrace event was dropped because the event id was " \
"missing. This is an 'info' stat rather than an error stat because " \
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index d6e86cf..ec014d7 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -43,6 +43,7 @@
#include "src/trace_processor/tables/android_tables_py.h"
#include "src/trace_processor/tables/counter_tables_py.h"
#include "src/trace_processor/tables/flow_tables_py.h"
+#include "src/trace_processor/tables/jit_tables_py.h"
#include "src/trace_processor/tables/memory_tables_py.h"
#include "src/trace_processor/tables/metadata_tables_py.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
@@ -761,6 +762,14 @@
return &v8_js_function_table_;
}
+ const tables::JitCodeTable& jit_code_table() const { return jit_code_table_; }
+ tables::JitCodeTable* mutable_jit_code_table() { return &jit_code_table_; }
+
+ const tables::JitFrameTable& jit_frame_table() const {
+ return jit_frame_table_;
+ }
+ tables::JitFrameTable* mutable_jit_frame_table() { return &jit_frame_table_; }
+
const tables::SurfaceFlingerLayersSnapshotTable&
surfaceflinger_layers_snapshot_table() const {
return surfaceflinger_layers_snapshot_table_;
@@ -950,8 +959,8 @@
&string_pool_, &uid_track_table_};
tables::ProcessTrackTable process_track_table_{&string_pool_, &track_table_};
tables::ThreadTrackTable thread_track_table_{&string_pool_, &track_table_};
- tables::LinuxDeviceTrackTable linux_device_track_table_{
- &string_pool_, &track_table_};
+ tables::LinuxDeviceTrackTable linux_device_track_table_{&string_pool_,
+ &track_table_};
// Track tables for counter events.
tables::CounterTrackTable counter_track_table_{&string_pool_, &track_table_};
@@ -1067,6 +1076,10 @@
tables::V8WasmScriptTable v8_wasm_script_table_{&string_pool_};
tables::V8JsFunctionTable v8_js_function_table_{&string_pool_};
+ // Jit tables
+ tables::JitCodeTable jit_code_table_{&string_pool_};
+ tables::JitFrameTable jit_frame_table_{&string_pool_};
+
// Winscope tables
tables::SurfaceFlingerLayersSnapshotTable
surfaceflinger_layers_snapshot_table_{&string_pool_};
@@ -1120,6 +1133,9 @@
template <>
struct std::hash<::perfetto::trace_processor::tables::HeapGraphObjectTable::Id>
: std::hash<::perfetto::trace_processor::BaseId> {};
+template <>
+struct std::hash<::perfetto::trace_processor::tables::JitCodeTable::Id>
+ : std::hash<::perfetto::trace_processor::BaseId> {};
template <>
struct std::hash<
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index b2fcffb..8339d95 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -20,6 +20,7 @@
"android_tables.py",
"counter_tables.py",
"flow_tables.py",
+ "jit_tables.py",
"memory_tables.py",
"metadata_tables.py",
"profiler_tables.py",
diff --git a/src/trace_processor/tables/jit_tables.py b/src/trace_processor/tables/jit_tables.py
new file mode 100644
index 0000000..aff2c61
--- /dev/null
+++ b/src/trace_processor/tables/jit_tables.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Contains tables related to Jitted code.
+
+These tables are WIP, the schema is not stable and you should not rely on them
+for any serious business just yet""
+"""
+
+from python.generators.trace_processor_table.public import Alias
+from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import CppOptional
+from python.generators.trace_processor_table.public import CppString
+from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.public import TableDoc
+from .profiler_tables import STACK_PROFILE_FRAME_TABLE
+
+JIT_CODE_TABLE = Table(
+ python_module=__file__,
+ class_name='JitCodeTable',
+ sql_name='__intrinsic_jit_code',
+ columns=[
+ C('create_ts', CppInt64()),
+ C('estimated_delete_ts', CppOptional(CppInt64())),
+ C('utid', CppUint32()),
+ C('start_address', CppInt64()),
+ C('size', CppInt64()),
+ C('function_name', CppString()),
+ C('native_code_base64', CppOptional(CppString())),
+ C('jit_code_id', Alias('id')),
+ ],
+ tabledoc=TableDoc(
+ doc="""
+ Represents a jitted code snippet
+ """,
+ group='jit',
+ columns={
+ 'create_ts': """Time this code was created / allocated""",
+ 'estimated_delete_ts':
+ ("""Time this code was destroyed / deallocated. This is an upper
+ bound, as we can only detect deletions indirectly when new code
+ is allocated overlapping existing one.
+ """),
+ 'utid': 'Thread that generated the code',
+ 'start_address': 'Start address for the generated code',
+ 'size': 'Size in bytes of the generated code',
+ 'function_name': 'Function name',
+ 'native_code_base64': 'Jitted code base64 encoded',
+ 'jit_code_id': 'Alias for id. Makes joins easier',
+ },
+ ),
+)
+
+JIT_FRAME_TABLE = Table(
+ python_module=__file__,
+ class_name='JitFrameTable',
+ sql_name='__intrinsic_jit_frame',
+ columns=[
+ C('jit_code_id', CppTableId(JIT_CODE_TABLE)),
+ C('frame_id', CppTableId(STACK_PROFILE_FRAME_TABLE)),
+ ],
+ tabledoc=TableDoc(
+ doc="""
+ Represents a jitted frame
+ """,
+ group='jit',
+ columns={
+ 'jit_code_id': 'Jitted code snipped the frame is in',
+ 'frame_id': 'Jitted frame',
+ },
+ ),
+)
+
+# Keep this list sorted.
+ALL_TABLES = [JIT_CODE_TABLE, JIT_FRAME_TABLE]
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index fd420b3..16c2665 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -17,6 +17,7 @@
#include "src/trace_processor/tables/android_tables_py.h"
#include "src/trace_processor/tables/counter_tables_py.h"
#include "src/trace_processor/tables/flow_tables_py.h"
+#include "src/trace_processor/tables/jit_tables_py.h"
#include "src/trace_processor/tables/memory_tables_py.h"
#include "src/trace_processor/tables/metadata_tables_py.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
@@ -43,6 +44,10 @@
// counter_tables_py.h
CounterTable::~CounterTable() = default;
+// jit_tables.py
+JitCodeTable::~JitCodeTable() = default;
+JitFrameTable::~JitFrameTable() = default;
+
// metadata_tables_py.h
RawTable::~RawTable() = default;
FtraceEventTable::~FtraceEventTable() = default;
diff --git a/src/trace_processor/tables/v8_tables.py b/src/trace_processor/tables/v8_tables.py
index 8fb7c01..76a8cda 100644
--- a/src/trace_processor/tables/v8_tables.py
+++ b/src/trace_processor/tables/v8_tables.py
@@ -35,7 +35,7 @@
V8_ISOLATE = Table(
python_module=__file__,
class_name='V8IsolateTable',
- sql_name='v8_isolate',
+ sql_name='__intrinsic_v8_isolate',
columns=[
C('upid', CppUint32()),
C('internal_isolate_id', CppInt32()),
@@ -45,7 +45,6 @@
C('code_range_size', CppOptional(CppInt64())),
C('shared_code_range', CppOptional(CppBool())),
C('embedded_blob_code_copy_start_address', CppOptional(CppInt64())),
- C('v8_isolate_id', Alias('id')),
],
tabledoc=TableDoc(
doc='Represents one Isolate instance',
@@ -72,8 +71,6 @@
'Used when short builtin calls are enabled, where embedded'
' builtins are copied into the CodeRange so calls can be'
' nearer.',
- 'v8_isolate_id':
- 'Alias for id. Makes joins easier',
},
),
)
@@ -81,14 +78,13 @@
V8_JS_SCRIPT = Table(
python_module=__file__,
class_name='V8JsScriptTable',
- sql_name='v8_js_script',
+ sql_name='__intrinsic_v8_js_script',
columns=[
C('v8_isolate_id', CppTableId(V8_ISOLATE)),
C('internal_script_id', CppInt32()),
C('script_type', CppString()),
C('name', CppString()),
C('source', CppOptional(CppString())),
- C('v8_js_script_id', Alias('id')),
],
tabledoc=TableDoc(
doc='Represents one Javascript script',
@@ -99,7 +95,6 @@
'script_type': '',
'name': '',
'source': 'Actual contents of the script.',
- 'v8_js_script_id': 'Alias for id. Makes joins easier',
},
),
)
@@ -107,13 +102,12 @@
V8_WASM_SCRIPT = Table(
python_module=__file__,
class_name='V8WasmScriptTable',
- sql_name='v8_wasm_script',
+ sql_name='__intrinsic_v8_wasm_script',
columns=[
C('v8_isolate_id', CppTableId(V8_ISOLATE)),
C('internal_script_id', CppInt32()),
C('url', CppString()),
C('source', CppOptional(CppString())),
- C('v8_wasm_script_id', Alias('id')),
],
tabledoc=TableDoc(
doc='Represents one WASM script',
@@ -123,7 +117,6 @@
'internal_script_id': 'Script id used by the V8 engine',
'url': 'URL of the source',
'source': 'Actual contents of the script.',
- 'v8_wasm_script_id': 'Alias for id. Makes joins easier',
},
),
)
@@ -131,15 +124,14 @@
V8_JS_FUNCTION = Table(
python_module=__file__,
class_name='V8JsFunctionTable',
- sql_name='v8_js_function',
+ sql_name='__intrinsic_v8_js_function',
columns=[
C('name', CppString()),
C('v8_js_script_id', CppTableId(V8_JS_SCRIPT)),
C('is_toplevel', CppBool()),
C('kind', CppString()),
C('line', CppOptional(CppUint32())),
- C('column', CppOptional(CppUint32())),
- C('v8_js_function_id', Alias('id')),
+ C('col', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='Represents a v8 Javascript function',
@@ -158,10 +150,8 @@
'Function kind (e.g. regular function or constructor)',
'line':
'Line in script where function is defined. Starts at 1',
- 'column':
+ 'col':
'Column in script where function is defined. Starts at 1',
- 'v8_js_function_id':
- 'Alias for id. Makes joins easier',
},
),
)
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 02bc3ae..d0d335f 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -53,6 +53,7 @@
#include "src/trace_processor/metrics/metrics.h"
#include "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"
#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/functions/base64.h"
#include "src/trace_processor/perfetto_sql/intrinsics/functions/clock_functions.h"
#include "src/trace_processor/perfetto_sql/intrinsics/functions/create_function.h"
#include "src/trace_processor/perfetto_sql/intrinsics/functions/create_view_function.h"
@@ -78,6 +79,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/interval_intersect.h"
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h"
#include "src/trace_processor/perfetto_sql/prelude/tables_views.h"
#include "src/trace_processor/perfetto_sql/stdlib/stdlib.h"
@@ -693,6 +695,11 @@
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
+ {
+ base::Status status = RegisterBase64Functions(*engine_.get());
+ if (!status.ok())
+ PERFETTO_ELOG("%s", status.c_message());
+ }
const TraceStorage* storage = context_.storage.get();
@@ -819,6 +826,9 @@
RegisterStaticTable(storage->v8_wasm_script_table());
RegisterStaticTable(storage->v8_js_function_table());
+ RegisterStaticTable(storage->jit_code_table());
+ RegisterStaticTable(storage->jit_frame_table());
+
RegisterStaticTable(storage->surfaceflinger_layers_snapshot_table());
RegisterStaticTable(storage->surfaceflinger_layer_table());
RegisterStaticTable(storage->surfaceflinger_transactions_table());
@@ -883,6 +893,8 @@
new ExperimentalFlatSlice(&context_)));
engine_->RegisterStaticTableFunction(
std::make_unique<DominatorTree>(context_.storage->mutable_string_pool()));
+ engine_->RegisterStaticTableFunction(std::make_unique<IntervalIntersect>(
+ context_.storage->mutable_string_pool()));
engine_->RegisterStaticTableFunction(
std::make_unique<Dfs>(context_.storage->mutable_string_pool()));
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 26d1ff5..9a89eb3 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -126,10 +126,11 @@
std::unique_ptr<Destructible> perf_data_tracker; // PerfDataTracker
std::unique_ptr<Destructible> content_analyzer; // ProtoContentAnalyzer
std::unique_ptr<Destructible>
- shell_transitions_tracker; // ShellTransitionsTracker
- std::unique_ptr<Destructible> v8_tracker; // V8Tracker
+ shell_transitions_tracker; // ShellTransitionsTracker
std::unique_ptr<Destructible>
ftrace_sched_tracker; // FtraceSchedEventTracker
+ std::unique_ptr<Destructible> v8_tracker; // V8Tracker
+ std::unique_ptr<Destructible> jit_tracker; // JitTracker
// These fields are trace readers which will be called by |forwarding_parser|
// once the format of the trace is discovered. They are placed here as they
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index a5347cf..7da78d7 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -99,6 +99,7 @@
from diff_tests.stdlib.counters.tests import StdlibCounterIntervals
from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
from diff_tests.stdlib.intervals.tests import StdlibIntervals
+from diff_tests.stdlib.intervals.intersect_tests import IntervalsIntersect
from diff_tests.stdlib.graphs.dominator_tree_tests import DominatorTree
from diff_tests.stdlib.graphs.search_tests import GraphSearchTests
from diff_tests.stdlib.linux.tests import LinuxStdlib
@@ -274,7 +275,9 @@
*SpanJoinSmoke(index_path, 'stdlib/span_join', 'SpanJoinSmoke').fetch(),
*StdlibCommon(index_path, 'stdlib/common', 'StdlibCommon').fetch(),
*StdlibIntervals(index_path, 'stdlib/intervals',
- 'StdlibIntervals').fetch(),
+ 'StdlibIntervalsIntersect').fetch(),
+ *IntervalsIntersect(index_path, 'stdlib/intervals',
+ 'StdlibIntervals').fetch(),
*Timestamps(index_path, 'stdlib/timestamps', 'Timestamps').fetch(),
] + chrome_stdlib_tests
diff --git a/test/trace_processor/diff_tests/metrics/android/android_boot.out b/test/trace_processor/diff_tests/metrics/android/android_boot.out
index 3d5c172..3bf13cc 100644
--- a/test/trace_processor/diff_tests/metrics/android/android_boot.out
+++ b/test/trace_processor/diff_tests/metrics/android/android_boot.out
@@ -40,7 +40,7 @@
native_alloc_gc_count: 0
explicit_gc_count: 0
alloc_gc_count: 0
- mb_per_ms_of_gc: 0.8829305684617433
+ mb_per_ms_of_gc: 0.8829305684617432
}
post_boot_gc_aggregation {
total_gc_count: 4
@@ -54,6 +54,6 @@
native_alloc_gc_count: 0
explicit_gc_count: 0
alloc_gc_count: 0
- mb_per_ms_of_gc: 0.8829305684617433
+ mb_per_ms_of_gc: 0.8829305684617432
}
}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/metrics/camera/tests.py b/test/trace_processor/diff_tests/metrics/camera/tests.py
index fecb492..999c69a 100644
--- a/test/trace_processor/diff_tests/metrics/camera/tests.py
+++ b/test/trace_processor/diff_tests/metrics/camera/tests.py
@@ -30,7 +30,7 @@
gc_rss_and_dma {
min: 47779840.0
max: 2536079360.0
- avg: 1464706457.7348418
+ avg: 1464706457.734843
}
}
"""))
diff --git a/test/trace_processor/diff_tests/metrics/chrome/frame_times_metric.out b/test/trace_processor/diff_tests/metrics/chrome/frame_times_metric.out
index 3ae3370..12a0fc0 100644
--- a/test/trace_processor/diff_tests/metrics/chrome/frame_times_metric.out
+++ b/test/trace_processor/diff_tests/metrics/chrome/frame_times_metric.out
@@ -1205,6 +1205,6 @@
exp_frame_time: 16.787
exp_frame_time: 16.623
exp_frame_time: 16.707
- avg_surface_fps: 59.86008821173197
- exp_avg_surface_fps: 59.861591661151287
+ avg_surface_fps: 59.860088211731984
+ exp_avg_surface_fps: 59.86159166115123
}
diff --git a/test/trace_processor/diff_tests/parser/parsing/tests.py b/test/trace_processor/diff_tests/parser/parsing/tests.py
index 5df7a52..8619bc3 100644
--- a/test/trace_processor/diff_tests/parser/parsing/tests.py
+++ b/test/trace_processor/diff_tests/parser/parsing/tests.py
@@ -1318,3 +1318,39 @@
"all_data_source_flushed_ns",12344
"all_data_source_flushed_ns",12345
"""))
+
+ def test_ftrace_abi_errors_skipped_zero_data_length(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ packet {
+ ftrace_stats {
+ phase: END_OF_TRACE
+ cpu_stats {
+ cpu: 0
+ entries: 14
+ overrun: 0
+ commit_overrun: 0
+ bytes_read: 840
+ oldest_event_ts: 86557.552705
+ now_ts: 86557.574310
+ dropped_events: 0
+ read_events: 199966062
+ }
+ kernel_symbols_parsed: 128611
+ kernel_symbols_mem_kb: 1322
+ ftrace_parse_errors: FTRACE_STATUS_ABI_ZERO_DATA_LENGTH
+ }
+ trusted_uid: 9999
+ trusted_packet_sequence_id: 2
+ trusted_pid: 1069
+ }
+ """),
+ query="""
+ select name, severity, value
+ from stats
+ where name = "ftrace_abi_errors_skipped_zero_data_length"
+ """,
+ out=Csv("""
+ "name","severity","value"
+ "ftrace_abi_errors_skipped_zero_data_length","info",1
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/intervals/intersect_tests.py b/test/trace_processor/diff_tests/stdlib/intervals/intersect_tests.py
new file mode 100644
index 0000000..b6e1500
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/intervals/intersect_tests.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python3
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class IntervalsIntersect(TestSuite):
+
+ def test_simple_inteval_intersect(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ # 0 1 2 3 4 5 6 7
+ # A: _ - - - - - - _
+ # B: - - _ - - _ - -
+ # res: _ - _ - - _ - _
+ query="""
+ INCLUDE PERFETTO MODULE intervals.intersect;
+
+ CREATE PERFETTO TABLE A AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 1, 6)
+ )
+ SELECT * FROM data;
+
+ CREATE PERFETTO TABLE B AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 0, 2),
+ (1, 3, 2),
+ (2, 6, 2)
+ )
+ SELECT * FROM data;
+
+ SELECT ts, dur, left_id, right_id
+ FROM _interval_intersect!(A, B)
+ ORDER BY ts;
+ """,
+ out=Csv("""
+ "ts","dur","left_id","right_id"
+ 1,1,0,0
+ 3,2,0,1
+ 6,1,0,2
+ """))
+
+ def test_simple_inteval_intersect_rev(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ # 0 1 2 3 4 5 6 7
+ # A: _ - - - - - - _
+ # B: - - _ - - _ - -
+ # res: _ - _ - - _ - _
+ query="""
+ INCLUDE PERFETTO MODULE intervals.intersect;
+
+ CREATE PERFETTO TABLE A AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 1, 6)
+ )
+ SELECT * FROM data;
+
+ CREATE PERFETTO TABLE B AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 0, 2),
+ (1, 3, 2),
+ (2, 6, 2)
+ )
+ SELECT * FROM data;
+
+ SELECT ts, dur, left_id, right_id
+ FROM _interval_intersect!(B, A)
+ ORDER BY ts;
+ """,
+ out=Csv("""
+ "ts","dur","left_id","right_id"
+ 1,1,0,0
+ 3,2,1,0
+ 6,1,2,0
+ """))
+
+ def test_no_overlap(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ # A: __-
+ # B: -__
+ # res: ___
+ query="""
+ INCLUDE PERFETTO MODULE intervals.intersect;
+
+ CREATE PERFETTO TABLE A AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 2, 1)
+ )
+ SELECT * FROM data;
+
+ CREATE PERFETTO TABLE B AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 0, 1)
+ )
+ SELECT * FROM data;
+
+ SELECT ts, dur, left_id, right_id
+ FROM _interval_intersect!(A, B)
+ ORDER BY ts;
+ """,
+ out=Csv("""
+ "ts","dur","left_id","right_id"
+ """))
+
+ def test_no_overlap_rev(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ # A: __-
+ # B: -__
+ # res: ___
+ query="""
+ INCLUDE PERFETTO MODULE intervals.intersect;
+
+ CREATE PERFETTO TABLE A AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 2, 1)
+ )
+ SELECT * FROM data;
+
+ CREATE PERFETTO TABLE B AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 0, 1)
+ )
+ SELECT * FROM data;
+
+ SELECT ts, dur, left_id, right_id
+ FROM _interval_intersect!(B, A)
+ ORDER BY ts;
+ """,
+ out=Csv("""
+ "ts","dur","left_id","right_id"
+ """))
+
+ def test_single_point_overlap(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ # A: _-
+ # B: -_
+ # res: __
+ query="""
+ INCLUDE PERFETTO MODULE intervals.intersect;
+
+ CREATE PERFETTO TABLE A AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 1, 1)
+ )
+ SELECT * FROM data;
+
+ CREATE PERFETTO TABLE B AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 0, 1)
+ )
+ SELECT * FROM data;
+
+ SELECT ts, dur, left_id, right_id
+ FROM _interval_intersect!(A, B)
+ ORDER BY ts;
+ """,
+ out=Csv("""
+ "ts","dur","left_id","right_id"
+ """))
+
+ def test_single_point_overlap_rev(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ # A: _-
+ # B: -_
+ # res: __
+ query="""
+ INCLUDE PERFETTO MODULE intervals.intersect;
+
+ CREATE PERFETTO TABLE A AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 1, 1)
+ )
+ SELECT * FROM data;
+
+ CREATE PERFETTO TABLE B AS
+ WITH data(id, ts, dur) AS (
+ VALUES
+ (0, 0, 1)
+ )
+ SELECT * FROM data;
+
+ SELECT ts, dur, left_id, right_id
+ FROM _interval_intersect!(B, A)
+ ORDER BY ts;
+ """,
+ out=Csv("""
+ "ts","dur","left_id","right_id"
+ """))
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/stdlib/sched/tests.py b/test/trace_processor/diff_tests/stdlib/sched/tests.py
index 2438dea..9324d51 100644
--- a/test/trace_processor/diff_tests/stdlib/sched/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/sched/tests.py
@@ -162,14 +162,14 @@
92000000000,0.000009,0.000071
"""))
- def test_sched_thread_time_in_state(self):
+ def test_sched_time_in_state_for_thread(self):
return DiffTestBlueprint(
trace=DataPath('example_android_trace_30s.pb'),
query="""
INCLUDE PERFETTO MODULE sched.time_in_state;
SELECT *
- FROM sched_thread_time_in_state
+ FROM sched_time_in_state_for_thread
ORDER BY utid, state
LIMIT 10;
""",
@@ -211,3 +211,37 @@
9,0,"[NULL]","[NULL]",99,"[NULL]","[NULL]"
10,0,"[NULL]",0,99,"[NULL]","[NULL]"
"""))
+
+ def test_sched_time_in_state_for_thread_in_interval(self):
+ return DiffTestBlueprint(
+ trace=DataPath('example_android_trace_30s.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE sched.time_in_state;
+
+ SELECT *
+ FROM sched_time_in_state_for_thread_in_interval(71039311397, 10000000000, 44);
+ """,
+ out=Csv("""
+ "state","io_wait","blocked_function","dur"
+ "S","[NULL]","[NULL]",1404466083
+ "Running","[NULL]","[NULL]",4655524
+ "D","[NULL]","[NULL]",563645
+ "R+","[NULL]","[NULL]",380156
+ """))
+
+ def test_sched_time_in_state_and_cpu_for_thread_in_interval(self):
+ return DiffTestBlueprint(
+ trace=DataPath('example_android_trace_30s.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE sched.time_in_state;
+
+ SELECT *
+ FROM sched_time_in_state_and_cpu_for_thread_in_interval(71039311397, 10000000000, 44);
+ """,
+ out=Csv("""
+ "state","io_wait","cpu","blocked_function","dur"
+ "S","[NULL]","[NULL]","[NULL]",1404466083
+ "Running","[NULL]",2,"[NULL]",4655524
+ "D","[NULL]","[NULL]","[NULL]",563645
+ "R+","[NULL]","[NULL]","[NULL]",380156
+ """))
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 02149a1..e43f4f9 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -232,13 +232,13 @@
# If updating the version, also update bazel/deps.bzl.
Dependency(
'buildtools/sqlite.zip',
- 'https://storage.googleapis.com/perfetto/sqlite-amalgamation-3390200.zip',
- '87775784f8b22d0d0f1d7811870d39feaa7896319c7c20b849a4181c5a50609b',
+ 'https://storage.googleapis.com/perfetto/sqlite-amalgamation-3440200.zip',
+ '833be89b53b3be8b40a2e3d5fedb635080e3edb204957244f3d6987c2bb2345f',
'all', 'all'),
Dependency(
'buildtools/sqlite_src',
'https://chromium.googlesource.com/external/github.com/sqlite/sqlite.git',
- '202b2a7b54ea2dd13a8a5adfd75523abe4dcf17f', # refs/tags/version-3.39.2.
+ 'c8f9803dc32bfee78a9ca2b1abbe39499729219b', # refs/tags/version-3.44.2.
'all',
'all'),
diff --git a/ui/src/frontend/sql/thread_state.ts b/ui/src/frontend/sql/thread_state.ts
index 24de373..e2740d5 100644
--- a/ui/src/frontend/sql/thread_state.ts
+++ b/ui/src/frontend/sql/thread_state.ts
@@ -72,17 +72,18 @@
// TODO(altimin): this probably should share some code with pivot tables when
// we actually get some pivot tables we like.
const query = await engine.query(`
- INCLUDE PERFETTO MODULE deprecated.v42.common.thread_states;
+ INCLUDE PERFETTO MODULE sched.time_in_state;
+ INCLUDE PERFETTO MODULE sched.states;
+ INCLUDE PERFETTO MODULE cpu.size;
SELECT
- state,
- raw_state as rawState,
- cpu_type as cpuType,
+ sched_state_io_to_human_readable_string(state, io_wait) as state,
+ state AS rawState,
+ cpu_guess_core_type(cpu) AS cpuType,
cpu,
- blocked_function as blockedFunction,
+ blocked_function AS blockedFunction,
dur
- FROM thread_state_summary_for_interval(${range.start}, ${range.duration}, ${
- utid});
+ FROM sched_time_in_state_and_cpu_for_thread_in_interval(${range.start}, ${range.duration}, ${utid});
`);
const it = query.iter({
state: STR,