Merge "Fix cast-function-type on windows" into main
diff --git a/Android.bp b/Android.bp
index 4e05485..a356ed1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11758,6 +11758,7 @@
"src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql",
"src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql",
"src/trace_processor/perfetto_sql/stdlib/android/slices.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startup_events.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_maxsdk28.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk29.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk33.sql",
diff --git a/BUILD b/BUILD
index 3500e82..91da2de 100644
--- a/BUILD
+++ b/BUILD
@@ -2219,6 +2219,7 @@
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_android_startup_startup",
srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startup_events.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_maxsdk28.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk29.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk33.sql",
diff --git a/CHANGELOG b/CHANGELOG
index 6c75a5c..a157ffd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,8 @@
* Added no_flush option to DataSourceDescriptor to avoid unnecessary IPC
roundtrips to flush data sources like track_event that rely uniquely on
server-side scraping.
+ * Added support for running on Linux & Android systems configured with 16K
+ pagetables.
Trace Processor:
*
UI:
diff --git a/docs/analysis/perfetto-sql-syntax.md b/docs/analysis/perfetto-sql-syntax.md
index cb9dd1b..3170ec8 100644
--- a/docs/analysis/perfetto-sql-syntax.md
+++ b/docs/analysis/perfetto-sql-syntax.md
@@ -42,6 +42,17 @@
FROM android_startups;
```
+For interactive development, the key can contain a wildcards:
+```sql
+-- Include all modules under android/.
+INCLUDE PERFETTO MODULE android.*;
+
+-- Or all stdlib modules:
+INCLUDE PERFETTO MODULE *;
+
+-- However, note, that both patterns are not allowed in stdlib.
+```
+
## Defining functions
`CREATE PEFETTO FUNCTION` allows functions to be defined in SQL. The syntax is
similar to the syntax in PostgreSQL or GoogleSQL.
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index 19c8029..1fb8196 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -172,7 +172,10 @@
libs = []
if ((is_android || is_linux) && !is_wasm) {
- ldflags += [ "-Wl,--build-id" ]
+ ldflags += [
+ "-Wl,--build-id",
+ "-Wl,-z,max-page-size=16384",
+ ]
}
if (is_clang || !is_win) { # Clang or GCC, but not MSVC.
@@ -319,9 +322,11 @@
include_dirs = win_msvc_inc_dirs # Defined in msvc.gni.
}
+ if (is_win) {
+ cflags += [ "/Zi" ]
+ }
if (is_debug) {
if (is_win) {
- cflags += [ "/Z7" ]
if (is_clang) {
# Required to see symbols in windbg when building with clang-cl.exe.
cflags += [ "-gcodeview-ghash" ]
@@ -394,7 +399,7 @@
}
}
-config("debug_symbols") {
+config("debug_noopt") {
cflags = []
if (is_win) {
cflags = [ "/Od" ]
diff --git a/gn/standalone/BUILDCONFIG.gn b/gn/standalone/BUILDCONFIG.gn
index 3a1119d..05ed548 100644
--- a/gn/standalone/BUILDCONFIG.gn
+++ b/gn/standalone/BUILDCONFIG.gn
@@ -63,7 +63,6 @@
target_cpu != host_cpu || target_os != host_os || target_triplet != ""
}
default_configs = [
- "//gn/standalone:debug_symbols",
"//gn/standalone:default",
"//gn/standalone:extra_warnings",
"//gn/standalone:no_exceptions",
@@ -74,13 +73,14 @@
"//gn/standalone:c++17",
]
-if (is_win) {
- default_configs += [ "//gn/standalone:win32_lean_and_mean" ]
+if (is_debug) {
+ default_configs += [ "//gn/standalone:debug_noopt" ]
+} else {
+ default_configs += [ "//gn/standalone:release" ]
}
-if (!is_debug) {
- default_configs -= [ "//gn/standalone:debug_symbols" ]
- default_configs += [ "//gn/standalone:release" ]
+if (is_win) {
+ default_configs += [ "//gn/standalone:win32_lean_and_mean" ]
}
set_defaults("source_set") {
diff --git a/gn/standalone/toolchain/BUILD.gn b/gn/standalone/toolchain/BUILD.gn
index e462bd5..236389c 100644
--- a/gn/standalone/toolchain/BUILD.gn
+++ b/gn/standalone/toolchain/BUILD.gn
@@ -490,8 +490,7 @@
pdbname = "$exename.pdb"
rspfile = "$exename.rsp"
- command =
- "$linker /nologo /OUT:$exename ${sys_lib_flags} /PDB:$pdbname @$rspfile"
+ command = "$linker /nologo /OUT:$exename ${sys_lib_flags} /DEBUG /PDB:$pdbname @$rspfile"
default_output_extension = ".exe"
default_output_dir = "{{root_out_dir}}"
outputs = [ exename ]
diff --git a/include/perfetto/ext/base/utils.h b/include/perfetto/ext/base/utils.h
index 99a9802..f3bfa1c 100644
--- a/include/perfetto/ext/base/utils.h
+++ b/include/perfetto/ext/base/utils.h
@@ -50,17 +50,21 @@
namespace perfetto {
namespace base {
-// Do not add new usages of kPageSize, consider using GetSysPageSize() below.
-// TODO(primiano): over time the semantic of kPageSize became too ambiguous.
-// Strictly speaking, this constant is incorrect on some new devices where the
-// page size can be 16K (e.g., crbug.com/1116576). Unfortunately too much code
-// ended up depending on kPageSize for purposes that are not strictly related
-// with the kernel's mm subsystem.
-constexpr size_t kPageSize = 4096;
+namespace internal {
+extern std::atomic<uint32_t> g_cached_page_size;
+uint32_t GetSysPageSizeSlowpath();
+} // namespace internal
// Returns the system's page size. Use this when dealing with mmap, madvise and
// similar mm-related syscalls.
-uint32_t GetSysPageSize();
+// This function might be called in hot paths. Avoid calling getpagesize() all
+// the times, in many implementations getpagesize() calls sysconf() which is
+// not cheap.
+inline uint32_t GetSysPageSize() {
+ const uint32_t page_size =
+ internal::g_cached_page_size.load(std::memory_order_relaxed);
+ return page_size != 0 ? page_size : internal::GetSysPageSizeSlowpath();
+}
template <typename T, size_t TSize>
constexpr size_t ArraySize(const T (&)[TSize]) {
@@ -85,10 +89,16 @@
}
// Round up |size| to a multiple of |alignment| (must be a power of two).
+inline constexpr size_t AlignUp(size_t size, size_t alignment) {
+ return (size + alignment - 1) & ~(alignment - 1);
+}
+
+// TODO(primiano): clean this up and move all existing usages to the constexpr
+// version above.
template <size_t alignment>
constexpr size_t AlignUp(size_t size) {
static_assert((alignment & (alignment - 1)) == 0, "alignment must be a pow2");
- return (size + alignment - 1) & ~(alignment - 1);
+ return AlignUp(size, alignment);
}
inline bool IsAgain(int err) {
diff --git a/include/perfetto/tracing/traced_value.h b/include/perfetto/tracing/traced_value.h
index fd8f791..1fbd830 100644
--- a/include/perfetto/tracing/traced_value.h
+++ b/include/perfetto/tracing/traced_value.h
@@ -157,7 +157,7 @@
// active at the same time. It's only allowed to call methods on the active
// scope.
// - When a scope creates a nested scope, the new scope becomes active.
- // - When a scope is destroyed, it's parent scope becomes active again.
+ // - When a scope is destroyed, its parent scope becomes active again.
//
// Typically users will have to create a scope only at the beginning of a
// conversion function and this scope should be destroyed at the end of it.
diff --git a/protos/perfetto/trace/android/surfaceflinger_layers.proto b/protos/perfetto/trace/android/surfaceflinger_layers.proto
index e6f1309..c7cc0cf 100644
--- a/protos/perfetto/trace/android/surfaceflinger_layers.proto
+++ b/protos/perfetto/trace/android/surfaceflinger_layers.proto
@@ -130,9 +130,9 @@
optional PositionProto requested_position = 12;
// The layer's size.
optional SizeProto size = 13;
- // The layer's crop in it's own bounds.
+ // The layer's crop in its own bounds.
optional RectProto crop = 14;
- // The layer's crop in it's parent's bounds.
+ // The layer's crop in its parent's bounds.
optional RectProto final_crop = 15 [deprecated = true];
optional bool is_opaque = 16;
optional bool invalidate = 17;
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index de2126d..f6b6cdc 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -4471,9 +4471,9 @@
optional PositionProto requested_position = 12;
// The layer's size.
optional SizeProto size = 13;
- // The layer's crop in it's own bounds.
+ // The layer's crop in its own bounds.
optional RectProto crop = 14;
- // The layer's crop in it's parent's bounds.
+ // The layer's crop in its parent's bounds.
optional RectProto final_crop = 15 [deprecated = true];
optional bool is_opaque = 16;
optional bool invalidate = 17;
@@ -10837,7 +10837,6 @@
RAIL_MODE_LOAD = 4;
}
-// Next id: 2
message ChromeRendererSchedulerState {
optional ChromeRAILMode rail_mode = 1;
diff --git a/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto b/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto
index 436cc79..043bbc0 100644
--- a/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto
+++ b/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto
@@ -31,7 +31,6 @@
RAIL_MODE_LOAD = 4;
}
-// Next id: 2
message ChromeRendererSchedulerState {
optional ChromeRAILMode rail_mode = 1;
diff --git a/python/generators/sql_processing/utils.py b/python/generators/sql_processing/utils.py
index e9247fe..ce37e2e 100644
--- a/python/generators/sql_processing/utils.py
+++ b/python/generators/sql_processing/utils.py
@@ -51,6 +51,9 @@
DROP_TABLE_VIEW_PATTERN = update_pattern(fr'^DROP (TABLE|VIEW) IF EXISTS '
fr'({NAME});$')
+INCLUDE_ALL_PATTERN = update_pattern(
+ fr'^INCLUDE PERFETTO MODULE [a-zA-Z0-9_\.]*\*;')
+
CREATE_FUNCTION_PATTERN = update_pattern(
# Function name.
fr"CREATE (OR REPLACE)? PERFETTO FUNCTION ({NAME}) "
@@ -163,6 +166,7 @@
errors.append('SELECT IMPORT is deprecated in trace processor. '
'Use INCLUDE PERFETTO MODULE instead.\n'
f'Offending file: {path}')
+
return errors
@@ -188,17 +192,18 @@
# Given SQL string check whether there is (not allowlisted) usage of
# CREATE TABLE {name} AS.
-def check_banned_create_table_as(sql: str, filename: str,
+def check_banned_create_table_as(sql: str, filename: str, stdlib_path: str,
allowlist: Dict[str, List[str]]) -> List[str]:
errors = []
for _, matches in match_pattern(CREATE_TABLE_AS_PATTERN, sql).items():
name = matches[0]
- if filename not in allowlist:
+ allowlist_key = filename[len(stdlib_path):]
+ if allowlist_key not in allowlist:
errors.append(f"CREATE TABLE '{name}' is deprecated. "
"Use CREATE PERFETTO TABLE instead.\n"
f"Offending file: {filename}\n")
continue
- if name not in allowlist[filename]:
+ if name not in allowlist[allowlist_key]:
errors.append(
f"Table '{name}' uses CREATE TABLE which is deprecated "
"and this table is not allowlisted. Use CREATE PERFETTO TABLE.\n"
@@ -215,3 +220,13 @@
"Use CREATE PERFETTO VIEW instead.\n"
f"Offending file: {filename}\n")
return errors
+
+
+# Given SQL string check whether there is usage of CREATE VIEW {name} AS.
+def check_banned_include_all(sql: str, filename: str) -> List[str]:
+ errors = []
+ for _, matches in match_pattern(INCLUDE_ALL_PATTERN, sql).items():
+ errors.append(
+ f"INCLUDE PERFETTO MODULE with wildcards is not allowed in stdlib. "
+ f"Import specific modules instead. Offending file: {filename}")
+ return errors
diff --git a/src/base/paged_memory_unittest.cc b/src/base/paged_memory_unittest.cc
index 7e3508b..388b5b2 100644
--- a/src/base/paged_memory_unittest.cc
+++ b/src/base/paged_memory_unittest.cc
@@ -35,14 +35,14 @@
TEST(PagedMemoryTest, Basic) {
const size_t kNumPages = 10;
- const size_t kSize = 4096 * kNumPages;
+ const size_t kSize = GetSysPageSize() * kNumPages;
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
void* ptr_raw = nullptr;
#endif
{
PagedMemory mem = PagedMemory::Allocate(kSize);
ASSERT_TRUE(mem.IsValid());
- ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(mem.Get()) % 4096);
+ ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(mem.Get()) % GetSysPageSize());
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
ptr_raw = mem.Get();
#endif
@@ -95,8 +95,8 @@
}
TEST(PagedMemoryTest, Uncommitted) {
- constexpr size_t kNumPages = 4096;
- constexpr size_t kSize = 4096 * kNumPages;
+ const size_t kNumPages = 4096;
+ const size_t kSize = GetSysPageSize() * kNumPages;
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
char* ptr_raw = nullptr;
#endif
@@ -156,14 +156,14 @@
TEST(PagedMemoryTest, AccessUncommittedMemoryTriggersASAN) {
EXPECT_DEATH_IF_SUPPORTED(
{
- constexpr size_t kNumPages = 4096;
- constexpr size_t kSize = 4096 * kNumPages;
+ const size_t kNumPages = 2000;
+ const size_t kSize = GetSysPageSize() * kNumPages;
PagedMemory mem =
PagedMemory::Allocate(kSize, PagedMemory::kDontCommit);
ASSERT_TRUE(mem.IsValid());
char* ptr_raw = reinterpret_cast<char*>(mem.Get());
// Only the first 1024 pages are mapped.
- constexpr size_t kMappedSize = 4096 * 1024;
+ const size_t kMappedSize = GetSysPageSize() * 1024;
ptr_raw[kMappedSize] = 'x';
abort();
},
diff --git a/src/base/utils.cc b/src/base/utils.cc
index 82386fa..ab3f390 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -136,6 +136,34 @@
namespace perfetto {
namespace base {
+namespace internal {
+
+std::atomic<uint32_t> g_cached_page_size{0};
+
+uint32_t GetSysPageSizeSlowpath() {
+ uint32_t page_size = 0;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ const int page_size_int = getpagesize();
+ // If sysconf() fails for obscure reasons (e.g. SELinux denial) assume the
+ // page size is 4KB. This is to avoid regressing subtle SDK usages, as old
+ // versions of this code had a static constant baked in.
+ page_size = static_cast<uint32_t>(page_size_int > 0 ? page_size_int : 4096);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ page_size = static_cast<uint32_t>(vm_page_size);
+#else
+ page_size = 4096;
+#endif
+
+ PERFETTO_CHECK(page_size > 0 && page_size % 4096 == 0);
+
+ // Races here are fine because any thread will write the same value.
+ g_cached_page_size.store(page_size, std::memory_order_relaxed);
+ return page_size;
+}
+
+} // namespace internal
+
void MaybeReleaseAllocatorMemToOS() {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
// mallopt() on Android requires SDK level 26. Many targets and embedders
@@ -153,27 +181,6 @@
#endif
}
-uint32_t GetSysPageSize() {
- ignore_result(kPageSize); // Just to keep the amalgamated build happy.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
- static std::atomic<uint32_t> page_size{0};
- // This function might be called in hot paths. Avoid calling getpagesize() all
- // the times, in many implementations getpagesize() calls sysconf() which is
- // not cheap.
- uint32_t cached_value = page_size.load(std::memory_order_relaxed);
- if (PERFETTO_UNLIKELY(cached_value == 0)) {
- cached_value = static_cast<uint32_t>(getpagesize());
- page_size.store(cached_value, std::memory_order_relaxed);
- }
- return cached_value;
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
- return static_cast<uint32_t>(vm_page_size);
-#else
- return 4096;
-#endif
-}
-
uid_t GetCurrentUserId() {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
diff --git a/src/profiling/memory/shared_ring_buffer.cc b/src/profiling/memory/shared_ring_buffer.cc
index 32d9b3c..871ea21 100644
--- a/src/profiling/memory/shared_ring_buffer.cc
+++ b/src/profiling/memory/shared_ring_buffer.cc
@@ -41,18 +41,21 @@
namespace {
-constexpr auto kMetaPageSize = base::kPageSize;
constexpr auto kAlignment = 8; // 64 bits to use aligned memcpy().
constexpr auto kHeaderSize = kAlignment;
-constexpr auto kGuardSize = base::kPageSize * 1024 * 16; // 64 MB.
+constexpr auto kGuardSize = 1024 * 1024 * 64; // 64 MB.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
constexpr auto kFDSeals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL;
#endif
+size_t meta_page_size() {
+ return base::GetSysPageSize();
+}
+
} // namespace
SharedRingBuffer::SharedRingBuffer(CreateFlag, size_t size) {
- size_t size_with_meta = size + kMetaPageSize;
+ size_t size_with_meta = size + meta_page_size();
base::ScopedFile fd;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
bool is_memfd = false;
@@ -101,7 +104,7 @@
"MetadataPage must be trivially destructible");
if (is_valid()) {
- size_t outer_size = kMetaPageSize + size_ * 2 + kGuardSize;
+ size_t outer_size = meta_page_size() + size_ * 2 + kGuardSize;
munmap(meta_, outer_size);
}
@@ -139,19 +142,19 @@
return;
}
auto size_with_meta = static_cast<size_t>(stat_buf.st_size);
- auto size = size_with_meta - kMetaPageSize;
+ auto size = size_with_meta - meta_page_size();
// |size_with_meta| must be a power of two number of pages + 1 page (for
// metadata).
- if (size_with_meta < 2 * base::kPageSize || size % base::kPageSize ||
- (size & (size - 1))) {
+ if (size_with_meta < 2 * base::GetSysPageSize() ||
+ size % base::GetSysPageSize() || (size & (size - 1))) {
PERFETTO_ELOG("SharedRingBuffer size is invalid (%zu)", size_with_meta);
return;
}
// First of all reserve the whole virtual region to fit the buffer twice
// + metadata page + red zone at the end.
- size_t outer_size = kMetaPageSize + size * 2 + kGuardSize;
+ size_t outer_size = meta_page_size() + size * 2 + kGuardSize;
uint8_t* region = reinterpret_cast<uint8_t*>(
mmap(nullptr, outer_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
if (region == MAP_FAILED) {
@@ -167,7 +170,7 @@
// [ METADATA ] [ RING BUFFER SHMEM ] [ RING BUFFER SHMEM ]
void* reg2 = mmap(region + size_with_meta, size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, *mem_fd,
- /*offset=*/kMetaPageSize);
+ /*offset=*/static_cast<ssize_t>(meta_page_size()));
if (reg1 != region || reg2 != region + size_with_meta) {
PERFETTO_PLOG("mmap(MAP_SHARED) failed");
@@ -176,7 +179,7 @@
}
set_size(size);
meta_ = reinterpret_cast<MetadataPage*>(region);
- mem_ = region + kMetaPageSize;
+ mem_ = region + meta_page_size();
mem_fd_ = std::move(mem_fd);
}
diff --git a/src/profiling/memory/shared_ring_buffer.h b/src/profiling/memory/shared_ring_buffer.h
index 9bc2e26..2a49da5 100644
--- a/src/profiling/memory/shared_ring_buffer.h
+++ b/src/profiling/memory/shared_ring_buffer.h
@@ -266,7 +266,7 @@
base::ScopedFile mem_fd_;
MetadataPage* meta_ = nullptr; // Start of the mmaped region.
- uint8_t* mem_ = nullptr; // Start of the contents (i.e. meta_ + kPageSize).
+ uint8_t* mem_ = nullptr; // Start of the contents (i.e. meta_ + pagesize).
// Size of the ring buffer contents, without including metadata or the 2nd
// mmap.
diff --git a/src/profiling/memory/shared_ring_buffer_fuzzer.cc b/src/profiling/memory/shared_ring_buffer_fuzzer.cc
index 2a597a9..46990a0 100644
--- a/src/profiling/memory/shared_ring_buffer_fuzzer.cc
+++ b/src/profiling/memory/shared_ring_buffer_fuzzer.cc
@@ -20,6 +20,7 @@
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/temp_file.h"
+#include "perfetto/ext/base/utils.h"
#include "src/profiling/memory/shared_ring_buffer.h"
namespace perfetto {
@@ -55,7 +56,7 @@
size_t payload_size = size - sizeof(SharedRingBuffer::MetadataPage);
const uint8_t* payload = data + sizeof(SharedRingBuffer::MetadataPage);
size_t payload_size_pages =
- (payload_size + base::kPageSize - 1) / base::kPageSize;
+ (payload_size + base::GetSysPageSize() - 1) / base::GetSysPageSize();
// Upsize test buffer to be 2^n data pages (precondition of the impl) + 1 page
// for the metadata.
size_t total_size_pages = 1 + RoundToPow2(payload_size_pages);
@@ -68,9 +69,10 @@
header.spinlock.poisoned = false;
PERFETTO_CHECK(ftruncate(*fd, static_cast<off_t>(total_size_pages *
- base::kPageSize)) == 0);
+ base::GetSysPageSize())) ==
+ 0);
PERFETTO_CHECK(base::WriteAll(*fd, &header, sizeof(header)) != -1);
- PERFETTO_CHECK(lseek(*fd, base::kPageSize, SEEK_SET) != -1);
+ PERFETTO_CHECK(lseek(*fd, base::GetSysPageSize(), SEEK_SET) != -1);
PERFETTO_CHECK(base::WriteAll(*fd, payload, payload_size) != -1);
auto buf = SharedRingBuffer::Attach(std::move(fd));
diff --git a/src/profiling/memory/shared_ring_buffer_unittest.cc b/src/profiling/memory/shared_ring_buffer_unittest.cc
index ff533f3..5128b7a 100644
--- a/src/profiling/memory/shared_ring_buffer_unittest.cc
+++ b/src/profiling/memory/shared_ring_buffer_unittest.cc
@@ -101,7 +101,7 @@
ASSERT_EQ(ToString(buf_and_size), data);
rd->EndRead(std::move(buf_and_size));
}
- data = std::string(base::kPageSize - sizeof(uint64_t), '#');
+ data = std::string(base::GetSysPageSize() - sizeof(uint64_t), '#');
for (int i = 0; i < 4; i++)
ASSERT_TRUE(TryWrite(wr, data.data(), data.size()));
@@ -146,7 +146,7 @@
}
TEST(SharedRingBufferTest, ReadShutdown) {
- constexpr auto kBufSize = base::kPageSize * 4;
+ const size_t kBufSize = base::GetSysPageSize() * 4;
std::optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
ASSERT_TRUE(wr);
SharedRingBuffer rd =
@@ -157,7 +157,7 @@
}
TEST(SharedRingBufferTest, WriteShutdown) {
- constexpr auto kBufSize = base::kPageSize * 4;
+ const size_t kBufSize = base::GetSysPageSize() * 4;
std::optional<SharedRingBuffer> rd = SharedRingBuffer::Create(kBufSize);
ASSERT_TRUE(rd);
SharedRingBuffer wr =
@@ -173,13 +173,13 @@
}
TEST(SharedRingBufferTest, SingleThreadSameInstance) {
- constexpr auto kBufSize = base::kPageSize * 4;
+ const size_t kBufSize = base::GetSysPageSize() * 4;
std::optional<SharedRingBuffer> buf = SharedRingBuffer::Create(kBufSize);
StructuredTest(&*buf, &*buf);
}
TEST(SharedRingBufferTest, SingleThreadAttach) {
- constexpr auto kBufSize = base::kPageSize * 4;
+ const size_t kBufSize = base::GetSysPageSize() * 4;
std::optional<SharedRingBuffer> buf1 = SharedRingBuffer::Create(kBufSize);
std::optional<SharedRingBuffer> buf2 =
SharedRingBuffer::Attach(base::ScopedFile(dup(buf1->fd())));
@@ -187,7 +187,7 @@
}
TEST(SharedRingBufferTest, MultiThreadingTest) {
- constexpr auto kBufSize = base::kPageSize * 1024; // 4 MB
+ const size_t kBufSize = base::GetSysPageSize() * 1024; // 4 MB
SharedRingBuffer rd = *SharedRingBuffer::Create(kBufSize);
SharedRingBuffer wr =
*SharedRingBuffer::Attach(base::ScopedFile(dup(rd.fd())));
@@ -201,7 +201,7 @@
while (!writers_enabled.load()) {
}
std::minstd_rand0 rnd_engine(static_cast<uint32_t>(thread_id));
- std::uniform_int_distribution<size_t> dist(1, base::kPageSize * 8);
+ std::uniform_int_distribution<size_t> dist(1, base::GetSysPageSize() * 8);
for (int i = 0; i < 1000; i++) {
size_t size = dist(rnd_engine);
ASSERT_GT(size, 0u);
@@ -255,13 +255,13 @@
}
TEST(SharedRingBufferTest, InvalidSize) {
- constexpr auto kBufSize = base::kPageSize * 4 + 1;
+ const size_t kBufSize = base::GetSysPageSize() * 4 + 1;
std::optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
EXPECT_EQ(wr, std::nullopt);
}
TEST(SharedRingBufferTest, EmptyWrite) {
- constexpr auto kBufSize = base::kPageSize * 4;
+ const size_t kBufSize = base::GetSysPageSize() * 4;
std::optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
ASSERT_TRUE(wr);
SharedRingBuffer::Buffer buf;
diff --git a/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc b/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc
index 62b82eb..fa81338 100644
--- a/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc
+++ b/src/profiling/memory/shared_ring_buffer_write_fuzzer.cc
@@ -20,6 +20,7 @@
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/temp_file.h"
+#include "perfetto/ext/base/utils.h"
#include "src/profiling/memory/shared_ring_buffer.h"
namespace perfetto {
@@ -60,7 +61,7 @@
size_t payload_size = size - sizeof(FuzzingInputHeader);
const uint8_t* payload = data + sizeof(FuzzingInputHeader);
size_t payload_size_pages =
- (payload_size + base::kPageSize - 1) / base::kPageSize;
+ (payload_size + base::GetSysPageSize() - 1) / base::GetSysPageSize();
// Upsize test buffer to be 2^n data pages (precondition of the impl) + 1 page
// for the metadata.
size_t total_size_pages = 1 + RoundToPow2(payload_size_pages);
@@ -73,10 +74,11 @@
metadata_page.spinlock.poisoned = false;
PERFETTO_CHECK(ftruncate(*fd, static_cast<off_t>(total_size_pages *
- base::kPageSize)) == 0);
+ base::GetSysPageSize())) ==
+ 0);
PERFETTO_CHECK(base::WriteAll(*fd, &metadata_page, sizeof(metadata_page)) !=
-1);
- PERFETTO_CHECK(lseek(*fd, base::kPageSize, SEEK_SET) != -1);
+ PERFETTO_CHECK(lseek(*fd, base::GetSysPageSize(), SEEK_SET) != -1);
PERFETTO_CHECK(base::WriteAll(*fd, payload, payload_size) != -1);
auto buf = SharedRingBuffer::Attach(std::move(fd));
diff --git a/src/profiling/perf/event_config.cc b/src/profiling/perf/event_config.cc
index 25b05c3..79df2ce 100644
--- a/src/profiling/perf/event_config.cc
+++ b/src/profiling/perf/event_config.cc
@@ -416,7 +416,7 @@
// TODO(rsavitski): for now, make an extremely conservative guess of an 8
// byte sample (stack sampling samples can be up to 64KB). This is most
// likely as good as no limit in practice.
- samples_per_tick_limit = *ring_buffer_pages * (base::kPageSize / 8);
+ samples_per_tick_limit = *ring_buffer_pages * (base::GetSysPageSize() / 8);
}
PERFETTO_DLOG("Capping samples (not records) per tick to [%" PRIu64 "]",
samples_per_tick_limit);
diff --git a/src/profiling/perf/event_reader.cc b/src/profiling/perf/event_reader.cc
index 2a40c6c..cb606f6 100644
--- a/src/profiling/perf/event_reader.cc
+++ b/src/profiling/perf/event_reader.cc
@@ -119,8 +119,8 @@
PerfRingBuffer ret;
// mmap request is one page larger than the buffer size (for the metadata).
- ret.data_buf_sz_ = data_page_count * base::kPageSize;
- ret.mmap_sz_ = ret.data_buf_sz_ + base::kPageSize;
+ ret.data_buf_sz_ = data_page_count * base::GetSysPageSize();
+ ret.mmap_sz_ = ret.data_buf_sz_ + base::GetSysPageSize();
// If PROT_WRITE, kernel won't overwrite unread samples.
void* mmap_addr = mmap(nullptr, ret.mmap_sz_, PROT_READ | PROT_WRITE,
@@ -132,8 +132,8 @@
// Expected layout is [ metadata page ] [ data pages ... ]
ret.metadata_page_ = reinterpret_cast<perf_event_mmap_page*>(mmap_addr);
- ret.data_buf_ = reinterpret_cast<char*>(mmap_addr) + base::kPageSize;
- PERFETTO_CHECK(ret.metadata_page_->data_offset == base::kPageSize);
+ ret.data_buf_ = reinterpret_cast<char*>(mmap_addr) + base::GetSysPageSize();
+ PERFETTO_CHECK(ret.metadata_page_->data_offset == base::GetSysPageSize());
PERFETTO_CHECK(ret.metadata_page_->data_size == ret.data_buf_sz_);
PERFETTO_DCHECK(IsPowerOfTwo(ret.data_buf_sz_));
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
index ee342b1..9e8d747 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
@@ -259,8 +259,8 @@
source = std::move(source_or.value());
} else if (auto* cst = std::get_if<PerfettoSqlParser::CreateTable>(
&parser.statement())) {
- RETURN_IF_ERROR(AddTracebackIfNeeded(
- RegisterRuntimeTable(cst->name, cst->sql), parser.statement_sql()));
+ RETURN_IF_ERROR(AddTracebackIfNeeded(ExecuteCreateTable(*cst),
+ parser.statement_sql()));
source = RewriteToDummySql(parser.statement_sql());
} else if (auto* create_view = std::get_if<PerfettoSqlParser::CreateView>(
&parser.statement())) {
@@ -367,6 +367,12 @@
auto* ctx = static_cast<CreatedFunction::Context*>(
sqlite_engine()->GetFunctionContext(prototype.function_name,
created_argc));
+ if (ctx && replace) {
+ // If the function already exists and we are replacing it, unregister it.
+ RETURN_IF_ERROR(UnregisterFunctionWithSqlite(
+ prototype.function_name.c_str(), created_argc));
+ ctx = nullptr;
+ }
if (!ctx) {
// We register the function with SQLite before we prepare the statement so
// the statement can reference the function itself, enabling recursive
@@ -384,31 +390,19 @@
std::move(return_type_str), std::move(sql));
}
-base::Status PerfettoSqlEngine::RegisterRuntimeTable(std::string name,
- SqlSource sql) {
- auto stmt_or = engine_->PrepareStatement(sql);
+base::Status PerfettoSqlEngine::ExecuteCreateTable(
+ const PerfettoSqlParser::CreateTable& create_table) {
+ auto stmt_or = engine_->PrepareStatement(create_table.sql);
RETURN_IF_ERROR(stmt_or.status());
SqliteEngine::PreparedStatement stmt = std::move(stmt_or);
- uint32_t columns =
- static_cast<uint32_t>(sqlite3_column_count(stmt.sqlite_stmt()));
- std::vector<std::string> column_names;
- for (uint32_t i = 0; i < columns; ++i) {
- std::string col_name =
- sqlite3_column_name(stmt.sqlite_stmt(), static_cast<int>(i));
- if (col_name.empty()) {
- return base::ErrStatus(
- "CREATE PERFETTO TABLE: column name must not be empty");
- }
- if (!std::isalpha(col_name.front()) ||
- !sql_argument::IsValidName(base::StringView(col_name))) {
- return base::ErrStatus(
- "Column name %s has to start with a letter and can only consists "
- "of alphanumeric characters and underscores.",
- col_name.c_str());
- }
- column_names.push_back(col_name);
- }
+ base::StatusOr<std::vector<std::string>> maybe_column_names =
+ GetColumnNamesFromSelectStatement(stmt, "CREATE PERFETTO TABLE");
+ RETURN_IF_ERROR(maybe_column_names.status());
+ std::vector<std::string> column_names = *maybe_column_names;
+
+ RETURN_IF_ERROR(ValidateColumnNames(column_names, create_table.schema,
+ "CREATE PERFETTO TABLE"));
size_t column_count = column_names.size();
auto table = std::make_unique<RuntimeTable>(pool_, std::move(column_names));
@@ -440,19 +434,33 @@
return base::ErrStatus(
"CREATE PERFETTO TABLE on column '%s' in table '%s': bytes "
"columns are not supported",
- sqlite3_column_name(stmt.sqlite_stmt(), int_i), name.c_str());
+ sqlite3_column_name(stmt.sqlite_stmt(), int_i),
+ create_table.name.c_str());
}
}
}
if (res != SQLITE_DONE) {
return base::ErrStatus("%s: SQLite error while creating table body: %s",
- name.c_str(), sqlite3_errmsg(engine_->db()));
+ create_table.name.c_str(),
+ sqlite3_errmsg(engine_->db()));
}
RETURN_IF_ERROR(table->AddColumnsAndOverlays(rows));
- runtime_tables_.Insert(name, std::move(table));
+ if (runtime_tables_.Find(create_table.name)) {
+ if (!create_table.replace) {
+ return base::ErrStatus("CREATE PERFETTO TABLE: table '%s' already exists",
+ create_table.name.c_str());
+ }
+
+ base::StackString<1024> drop("DROP TABLE %s", create_table.name.c_str());
+ RETURN_IF_ERROR(
+ Execute(SqlSource::FromTraceProcessorImplementation(drop.ToStdString()))
+ .status());
+ }
+
+ runtime_tables_.Insert(create_table.name, std::move(table));
base::StackString<1024> create("CREATE VIRTUAL TABLE %s USING runtime_table",
- name.c_str());
+ create_table.name.c_str());
return Execute(
SqlSource::FromTraceProcessorImplementation(create.ToStdString()))
.status();
@@ -460,7 +468,30 @@
base::Status PerfettoSqlEngine::ExecuteCreateView(
const PerfettoSqlParser::CreateView& create_view) {
- RETURN_IF_ERROR(Execute(create_view.sql).status());
+ // Verify that the underlying SQL statement is valid.
+ auto stmt = sqlite_engine()->PrepareStatement(create_view.select_sql);
+ RETURN_IF_ERROR(stmt.status());
+
+ if (create_view.replace) {
+ base::StackString<1024> drop_if_exists("DROP VIEW IF EXISTS %s",
+ create_view.name.c_str());
+ RETURN_IF_ERROR(Execute(SqlSource::FromTraceProcessorImplementation(
+ drop_if_exists.ToStdString()))
+ .status());
+ }
+
+ // If the schema is specified, verify that the column names match it.
+ if (!create_view.schema.empty()) {
+ base::StatusOr<std::vector<std::string>> maybe_column_names =
+ GetColumnNamesFromSelectStatement(stmt, "CREATE PERFETTO VIEW");
+ RETURN_IF_ERROR(maybe_column_names.status());
+ std::vector<std::string> column_names = *maybe_column_names;
+
+ RETURN_IF_ERROR(ValidateColumnNames(column_names, create_view.schema,
+ "CREATE PERFETTO VIEW"));
+ }
+
+ RETURN_IF_ERROR(Execute(create_view.create_view_sql).status());
runtime_views_count_++;
return base::OkStatus();
}
@@ -483,23 +514,60 @@
std::string key = include.key;
PERFETTO_TP_TRACE(metatrace::Category::QUERY_TIMELINE, "Include",
[key](metatrace::Record* r) { r->AddArg("Module", key); });
- std::string module_name = sql_modules::GetModuleName(key);
- auto module = FindModule(module_name);
- if (!module)
- return base::ErrStatus("INCLUDE: Unknown module name provided - %s",
- key.c_str());
- auto module_file = module->include_key_to_file.Find(key);
- if (!module_file) {
- return base::ErrStatus("INCLUDE: Unknown filename provided - %s",
- key.c_str());
- }
- // INCLUDE is noop for already included files.
- if (module_file->included) {
+ if (key == "*") {
+ for (auto moduleIt = modules_.GetIterator(); moduleIt; ++moduleIt) {
+ RETURN_IF_ERROR(IncludeModuleImpl(moduleIt.value(), key, parser));
+ }
return base::OkStatus();
}
- auto it = Execute(SqlSource::FromModuleInclude(module_file->sql, key));
+ std::string module_name = sql_modules::GetModuleName(key);
+ auto module = FindModule(module_name);
+ if (!module) {
+ return base::ErrStatus("INCLUDE: Unknown module name provided - %s",
+ key.c_str());
+ }
+ return IncludeModuleImpl(*module, key, parser);
+}
+
+base::Status PerfettoSqlEngine::IncludeModuleImpl(
+ sql_modules::RegisteredModule& module,
+ const std::string& key,
+ const PerfettoSqlParser& parser) {
+ if (!key.empty() && key.back() == '*') {
+ // If the key ends with a wildcard, iterate through all the keys in the
+ // module and include matching ones.
+ std::string prefix = key.substr(0, key.size() - 1);
+ for (auto fileIt = module.include_key_to_file.GetIterator(); fileIt;
+ ++fileIt) {
+ if (!base::StartsWith(fileIt.key(), prefix))
+ continue;
+ PERFETTO_TP_TRACE(
+ metatrace::Category::QUERY_TIMELINE,
+ "Include (expanded from wildcard)",
+ [&](metatrace::Record* r) { r->AddArg("Module", fileIt.key()); });
+ RETURN_IF_ERROR(IncludeFileImpl(fileIt.value(), fileIt.key(), parser));
+ }
+ return base::OkStatus();
+ }
+ auto* module_file = module.include_key_to_file.Find(key);
+ if (!module_file) {
+ return base::ErrStatus("INCLUDE: unknown module '%s'", key.c_str());
+ }
+ return IncludeFileImpl(*module_file, key, parser);
+}
+
+base::Status PerfettoSqlEngine::IncludeFileImpl(
+ sql_modules::RegisteredModule::ModuleFile& file,
+ const std::string& key,
+ const PerfettoSqlParser& parser) {
+ // INCLUDE is noop for already included files.
+ if (file.included) {
+ return base::OkStatus();
+ }
+
+ auto it = Execute(SqlSource::FromModuleInclude(file.sql, key));
if (!it.status().ok()) {
return base::ErrStatus("%s%s",
parser.statement_sql().AsTraceback(0).c_str(),
@@ -507,8 +575,7 @@
}
if (it->statement_count_with_output > 0)
return base::ErrStatus("INCLUDE: Included module returning values.");
- module_file->included = true;
-
+ file.included = true;
return base::OkStatus();
}
@@ -684,5 +751,96 @@
PERFETTO_CHECK(runtime_table_fn_states_.Erase(base::ToLower(name)));
}
+base::Status PerfettoSqlEngine::UnregisterFunctionWithSqlite(const char* name,
+ int argc) {
+ return engine_->UnregisterFunction(name, argc);
+}
+
+base::StatusOr<std::vector<std::string>>
+PerfettoSqlEngine::GetColumnNamesFromSelectStatement(
+ const SqliteEngine::PreparedStatement& stmt,
+ const char* tag) const {
+ uint32_t columns =
+ static_cast<uint32_t>(sqlite3_column_count(stmt.sqlite_stmt()));
+ std::vector<std::string> column_names;
+ for (uint32_t i = 0; i < columns; ++i) {
+ std::string col_name =
+ sqlite3_column_name(stmt.sqlite_stmt(), static_cast<int>(i));
+ if (col_name.empty()) {
+ return base::ErrStatus("%s: column %d: name must not be empty", tag, i);
+ }
+ if (!std::isalpha(col_name.front())) {
+ return base::ErrStatus(
+ "%s: Column %i: name '%s' has to start with a letter.", tag, i,
+ col_name.c_str());
+ }
+ if (!sql_argument::IsValidName(base::StringView(col_name))) {
+ return base::ErrStatus(
+ "%s: Column %i: name '%s' has to contain only alphanumeric "
+ "characters and underscores.",
+ tag, i, col_name.c_str());
+ }
+ column_names.push_back(col_name);
+ }
+ return column_names;
+}
+
+base::Status PerfettoSqlEngine::ValidateColumnNames(
+ const std::vector<std::string>& column_names,
+ const std::vector<sql_argument::ArgumentDefinition>& schema,
+ const char* tag) const {
+ // If the user has not provided a schema, we have nothing to validate.
+ if (schema.empty()) {
+ return base::OkStatus();
+ }
+
+ std::vector<std::string> columns_missing_from_query;
+ std::vector<std::string> columns_missing_from_schema;
+
+ for (const std::string& name : column_names) {
+ bool present =
+ std::find_if(schema.begin(), schema.end(), [&name](const auto& arg) {
+ return arg.name() == base::StringView(name);
+ }) != schema.end();
+ if (!present) {
+ columns_missing_from_schema.push_back(name);
+ }
+ }
+
+ for (const auto& arg : schema) {
+ bool present = std::find_if(column_names.begin(), column_names.end(),
+ [&arg](const std::string& name) {
+ return arg.name() == base::StringView(name);
+ }) != column_names.end();
+ if (!present) {
+ columns_missing_from_query.push_back(arg.name().ToStdString());
+ }
+ }
+
+ if (columns_missing_from_query.empty() &&
+ columns_missing_from_schema.empty()) {
+ return base::OkStatus();
+ }
+
+ if (columns_missing_from_query.empty()) {
+ return base::ErrStatus(
+ "%s: the following columns are missing from the schema: %s", tag,
+ base::Join(columns_missing_from_schema, ", ").c_str());
+ }
+
+ if (columns_missing_from_schema.empty()) {
+ return base::ErrStatus(
+ "%s: the following columns are declared in the schema, but do not "
+ "exist: %s",
+ tag, base::Join(columns_missing_from_query, ", ").c_str());
+ }
+
+ return base::ErrStatus(
+ "%s: the following columns are declared in the schema, but do not exist: "
+ "%s; and the folowing columns exist, but are not declared: %s",
+ tag, base::Join(columns_missing_from_query, ", ").c_str(),
+ base::Join(columns_missing_from_schema, ", ").c_str());
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
index bb34a08..23f47dc 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h
@@ -163,8 +163,9 @@
base::Status ExecuteInclude(const PerfettoSqlParser::Include&,
const PerfettoSqlParser& parser);
- // Registers a SQL-defined trace processor C++ table with SQLite.
- base::Status RegisterRuntimeTable(std::string name, SqlSource sql);
+ // Creates a runtime table and registers it with SQLite.
+ base::Status ExecuteCreateTable(
+ const PerfettoSqlParser::CreateTable& create_table);
base::Status ExecuteCreateView(const PerfettoSqlParser::CreateView&);
@@ -177,6 +178,34 @@
std::unique_ptr<typename Function::Context> ctx,
bool deterministic = true);
+ base::Status UnregisterFunctionWithSqlite(const char* name, int argc);
+
+ // Get the column names from a statement.
+ // |operator_name| is used in the error message if the statement is invalid.
+ base::StatusOr<std::vector<std::string>> GetColumnNamesFromSelectStatement(
+ const SqliteEngine::PreparedStatement& stmt,
+ const char* tag) const;
+
+ // Validates that the column names in |column_names| match the |schema|.
+ // |operator_name| is used in the error message if the statement is invalid.
+ base::Status ValidateColumnNames(
+ const std::vector<std::string>& column_names,
+ const std::vector<sql_argument::ArgumentDefinition>& schema,
+ const char* operator_name) const;
+
+ // Given a module and a key, include the correct file(s) from the module.
+ // The key can contain a wildcard to include all files in the module with the
+ // matching prefix.
+ base::Status IncludeModuleImpl(sql_modules::RegisteredModule& module,
+ const std::string& key,
+ const PerfettoSqlParser& parser);
+
+ // Import a given file.
+ base::Status IncludeFileImpl(
+ sql_modules::RegisteredModule::ModuleFile& module,
+ const std::string& key,
+ const PerfettoSqlParser& parser);
+
std::unique_ptr<QueryCache> query_cache_;
StringPool* pool_ = nullptr;
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine_unittest.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine_unittest.cc
index 141ff51..784ef34 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine_unittest.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine_unittest.cc
@@ -29,29 +29,46 @@
PerfettoSqlEngine engine_{&pool_};
};
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoFunctionSmoke) {
+sql_modules::RegisteredModule CreateTestModule(
+ std::vector<std::pair<std::string, std::string>> files) {
+ sql_modules::RegisteredModule result;
+ for (auto& file : files) {
+ result.include_key_to_file[file.first] =
+ sql_modules::RegisteredModule::ModuleFile{file.second, false};
+ }
+ return result;
+}
+
+// These are the smoke tests for the perfetto SQL engine, focusing on
+// ensuring that the correct statements do not return an error and that
+// incorrect statements do.
+//
+// Functional tests are covered by the diff tests in
+// test/trace_processor/diff_tests/syntax/perfetto_sql.
+
+TEST_F(PerfettoSqlEngineTest, Function_Create) {
auto res = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE PERFETTO FUNCTION foo() RETURNS INT AS select 1"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
res = engine_.Execute(
SqlSource::FromExecuteQuery("creatE PeRfEttO FUNCTION foo(x INT, y LONG) "
"RETURNS INT AS select :x + :y"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
}
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoFunctionArgs) {
+TEST_F(PerfettoSqlEngineTest, Function_CreateWithArgs) {
auto res = engine_.ExecuteUntilLastStatement(
SqlSource::FromExecuteQuery("creatE PeRfEttO FUNCTION foo(x INT, y LONG) "
"RETURNS INT AS select $x + $y;"
"SELECT foo(1, 2)"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
ASSERT_FALSE(res->stmt.IsDone());
ASSERT_EQ(sqlite3_column_int64(res->stmt.sqlite_stmt(), 0), 3);
ASSERT_FALSE(res->stmt.Step());
}
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoFunctionError) {
+TEST_F(PerfettoSqlEngineTest, Function_Invalid) {
auto res = engine_.ExecuteUntilLastStatement(
SqlSource::FromExecuteQuery("creatE PeRfEttO FUNCTION foo(x INT, y LONG) "
"AS select $x + $y;"
@@ -59,44 +76,32 @@
ASSERT_FALSE(res.ok());
}
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoTableSmoke) {
+TEST_F(PerfettoSqlEngineTest, Function_Duplicates) {
auto res = engine_.Execute(SqlSource::FromExecuteQuery(
- "CREATE PERFETTO TABLE foo AS SELECT 42 AS bar"));
- ASSERT_TRUE(res.ok());
+ "CREATE PERFETTO FUNCTION foo() RETURNS INT AS SELECT 1"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO FUNCTION foo() RETURNS INT AS SELECT 2"));
+ ASSERT_FALSE(res.ok());
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE OR REPLACE PERFETTO FUNCTION foo() RETURNS INT AS SELECT 3"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
}
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoTableStringSmoke) {
- auto res = engine_.Execute(SqlSource::FromExecuteQuery(
- "CREATE PERFETTO TABLE foo AS SELECT 'foo' AS bar"));
- ASSERT_TRUE(res.ok());
-}
-
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoTableDrop) {
- auto res_create = engine_.Execute(SqlSource::FromExecuteQuery(
- "CREATE PERFETTO TABLE foo AS SELECT 'foo' AS bar"));
- ASSERT_TRUE(res_create.ok());
-
- auto res_drop =
- engine_.Execute(SqlSource::FromExecuteQuery("DROP TABLE foo"));
- ASSERT_TRUE(res_drop.ok());
-}
-
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoTableValues) {
- auto res = engine_.ExecuteUntilLastStatement(
- SqlSource::FromExecuteQuery("creatE PeRfEttO TABLE foo AS "
- "SELECT 42 as bar;"
- "SELECT * from foo"));
- ASSERT_TRUE(res.ok());
- ASSERT_FALSE(res->stmt.IsDone());
- ASSERT_EQ(sqlite3_column_int64(res->stmt.sqlite_stmt(), 0), 42);
- ASSERT_FALSE(res->stmt.Step());
-}
-
-TEST_F(PerfettoSqlEngineTest, CreateTableFunctionDupe) {
+TEST_F(PerfettoSqlEngineTest, TableFunction_Create) {
auto res = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE PERFETTO FUNCTION foo() RETURNS TABLE(x INT) AS "
"select 1 AS x"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+}
+
+TEST_F(PerfettoSqlEngineTest, TableFunction_Duplicates) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO FUNCTION foo() RETURNS TABLE(x INT) AS "
+ "select 1 AS x"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
res = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE PERFETTO FUNCTION foo() RETURNS TABLE(x INT) AS "
@@ -106,26 +111,117 @@
res = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE OR REPLACE PERFETTO FUNCTION foo() RETURNS TABLE(x INT) AS "
"select 2 AS x"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
}
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoViewSmoke) {
+TEST_F(PerfettoSqlEngineTest, Table_Create) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo AS SELECT 42 AS bar"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+}
+
+TEST_F(PerfettoSqlEngineTest, Table_StringColumns) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo AS SELECT 'foo' AS bar"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+}
+
+TEST_F(PerfettoSqlEngineTest, Table_Schema) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo(bar INT) AS SELECT 42 AS bar"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo2(bar INT) AS SELECT 42 AS bar; SELECT 1"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+}
+
+TEST_F(PerfettoSqlEngineTest, Table_IncorrectSchema) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo(x INT) AS SELECT 1 as y"));
+ ASSERT_FALSE(res.ok());
+ EXPECT_THAT(
+ res.status().c_message(),
+ testing::EndsWith("CREATE PERFETTO TABLE: the following columns are "
+ "declared in the schema, but do not exist: x; and the "
+ "folowing columns exist, but are not declared: y"));
+}
+
+TEST_F(PerfettoSqlEngineTest, Table_Drop) {
+ auto res_create = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo AS SELECT 'foo' AS bar"));
+ ASSERT_TRUE(res_create.ok());
+
+ auto res_drop =
+ engine_.Execute(SqlSource::FromExecuteQuery("DROP TABLE foo"));
+ ASSERT_TRUE(res_drop.ok());
+}
+
+TEST_F(PerfettoSqlEngineTest, Table_Duplicates) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo AS SELECT 1 as bar"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO TABLE foo AS SELECT 1 as bar"));
+ ASSERT_FALSE(res.ok());
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE OR REPLACE PERFETTO TABLE foo AS SELECT 1 as bar"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+}
+
+TEST_F(PerfettoSqlEngineTest, View_Create) {
auto res = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE PERFETTO VIEW foo AS SELECT 42 AS bar"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
}
-TEST_F(PerfettoSqlEngineTest, CreatePerfettoViewWithSchemaSmoke) {
+TEST_F(PerfettoSqlEngineTest, View_Schema) {
auto res = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE PERFETTO VIEW foo(bar INT) AS SELECT 42 AS bar"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
res = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE PERFETTO VIEW foo2(bar INT) AS SELECT 42 AS bar; SELECT 1"));
- ASSERT_TRUE(res.ok());
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
}
-TEST_F(PerfettoSqlEngineTest, CreateMacro) {
+TEST_F(PerfettoSqlEngineTest, View_Drop) {
+ auto res_create = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO VIEW foo AS SELECT 'foo' AS bar"));
+ ASSERT_TRUE(res_create.ok());
+
+ auto res_drop = engine_.Execute(SqlSource::FromExecuteQuery("DROP VIEW foo"));
+ ASSERT_TRUE(res_drop.ok());
+}
+
+TEST_F(PerfettoSqlEngineTest, View_IncorrectSchema) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO VIEW foo(x INT) AS SELECT 1 as y"));
+ ASSERT_FALSE(res.ok());
+ EXPECT_THAT(
+ res.status().c_message(),
+ testing::EndsWith("CREATE PERFETTO VIEW: the following columns are "
+ "declared in the schema, but do not exist: x; and the "
+ "folowing columns exist, but are not declared: y"));
+}
+
+TEST_F(PerfettoSqlEngineTest, View_Duplicates) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO VIEW foo AS SELECT 1 as bar"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO VIEW foo AS SELECT 1 as bar"));
+ ASSERT_FALSE(res.ok());
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE OR REPLACE PERFETTO VIEW foo AS SELECT 1 as bar"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+}
+
+TEST_F(PerfettoSqlEngineTest, Macro_Create) {
auto res_create = engine_.Execute(SqlSource::FromExecuteQuery(
"CREATE PERFETTO MACRO foo() RETURNS TableOrSubquery AS select 42 AS x"));
ASSERT_TRUE(res_create.ok()) << res_create.status().c_message();
@@ -143,6 +239,61 @@
ASSERT_FALSE(res->stmt.Step());
}
+TEST_F(PerfettoSqlEngineTest, Macro_Duplicates) {
+ auto res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO MACRO foo() RETURNS TableOrSubquery AS select 42 AS x"));
+ ASSERT_TRUE(res.ok()) << res.status().c_message();
+
+ res = engine_.Execute(SqlSource::FromExecuteQuery(
+ "CREATE PERFETTO MACRO foo() RETURNS TableOrSubquery AS select 42 AS x"));
+ ASSERT_FALSE(res.ok());
+
+ res = engine_.Execute(
+ SqlSource::FromExecuteQuery("CREATE OR REPLACE PERFETTO MACRO foo() "
+ "RETURNS TableOrSubquery AS select 42 AS x"));
+ ASSERT_TRUE(res.ok());
+}
+
+TEST_F(PerfettoSqlEngineTest, Include_All) {
+ engine_.RegisterModule(
+ "foo", CreateTestModule(
+ {{"foo.foo", "CREATE PERFETTO TABLE foo AS SELECT 42 AS x"}}));
+ engine_.RegisterModule(
+ "bar",
+ CreateTestModule(
+ {{"bar.bar", "CREATE PERFETTO TABLE bar AS SELECT 42 AS x "}}));
+
+ auto res_create =
+ engine_.Execute(SqlSource::FromExecuteQuery("INCLUDE PERFETTO MODULE *"));
+ ASSERT_TRUE(res_create.ok()) << res_create.status().c_message();
+ ASSERT_TRUE(
+ engine_.FindModule("foo")->include_key_to_file["foo.foo"].included);
+ ASSERT_TRUE(
+ engine_.FindModule("bar")->include_key_to_file["bar.bar"].included);
+}
+
+TEST_F(PerfettoSqlEngineTest, Include_Module) {
+ engine_.RegisterModule(
+ "foo", CreateTestModule({
+ {"foo.foo1", "CREATE PERFETTO TABLE foo1 AS SELECT 42 AS x"},
+ {"foo.foo2", "CREATE PERFETTO TABLE foo2 AS SELECT 42 AS x"},
+ }));
+ engine_.RegisterModule(
+ "bar",
+ CreateTestModule(
+ {{"bar.bar", "CREATE PERFETTO TABLE bar AS SELECT 42 AS x "}}));
+
+ auto res_create = engine_.Execute(
+ SqlSource::FromExecuteQuery("INCLUDE PERFETTO MODULE foo.*"));
+ ASSERT_TRUE(res_create.ok()) << res_create.status().c_message();
+ ASSERT_TRUE(
+ engine_.FindModule("foo")->include_key_to_file["foo.foo1"].included);
+ ASSERT_TRUE(
+ engine_.FindModule("foo")->include_key_to_file["foo.foo2"].included);
+ ASSERT_FALSE(
+ engine_.FindModule("bar")->include_key_to_file["bar.bar"].included);
+}
+
} // namespace
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
index 0666a6f..3063fc3 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
@@ -80,7 +80,18 @@
}
bool ValidateModuleName(const std::string& name) {
+ if (name.empty()) {
+ return false;
+ }
+
std::vector<std::string> packages = base::SplitString(name, ".");
+
+ // The last part of the path can be a wildcard.
+ if (!packages.empty() && packages.back() == "*") {
+ packages.pop_back();
+ }
+
+ // The rest of the path must be valid words.
return std::find_if(packages.begin(), packages.end(),
std::not_fn(IsValidModuleWord)) == packages.end();
}
@@ -190,21 +201,20 @@
break;
case State::kCreateOrReplacePerfetto:
case State::kCreatePerfetto:
+ bool replace = state == State::kCreateOrReplacePerfetto;
if (TokenIsCustomKeyword("function", token)) {
- return ParseCreatePerfettoFunction(
- state == State::kCreateOrReplacePerfetto, *first_non_space_token);
+ return ParseCreatePerfettoFunction(replace, *first_non_space_token);
}
if (TokenIsSqliteKeyword("table", token)) {
- return ParseCreatePerfettoTableOrView(*first_non_space_token,
+ return ParseCreatePerfettoTableOrView(replace, *first_non_space_token,
TableOrView::kTable);
}
if (TokenIsSqliteKeyword("view", token)) {
- return ParseCreatePerfettoTableOrView(*first_non_space_token,
+ return ParseCreatePerfettoTableOrView(replace, *first_non_space_token,
TableOrView::kView);
}
if (TokenIsCustomKeyword("macro", token)) {
- return ParseCreatePerfettoMacro(state ==
- State::kCreateOrReplacePerfetto);
+ return ParseCreatePerfettoMacro(replace);
}
base::StackString<1024> err(
"Expected 'FUNCTION', 'TABLE' or 'MACRO' after 'CREATE PERFETTO', "
@@ -223,8 +233,8 @@
if (!ValidateModuleName(key)) {
base::StackString<1024> err(
- "Only alphanumeric characters, dots and underscores allowed in include "
- "keys: '%s'",
+ "Include key should be a dot-separated list of module names, with the"
+ "last name optionally being a wildcard: '%s'",
key.c_str());
return ErrorAtToken(tok, err.c_str());
}
@@ -235,6 +245,7 @@
}
bool PerfettoSqlParser::ParseCreatePerfettoTableOrView(
+ bool replace,
Token first_non_space_token,
TableOrView table_or_view) {
Token table_name = tokenizer_.NextNonWhitespace();
@@ -270,7 +281,7 @@
Token terminal = tokenizer_.NextTerminal();
switch (table_or_view) {
case TableOrView::kTable:
- statement_ = CreateTable{std::move(name),
+ statement_ = CreateTable{replace, std::move(name),
tokenizer_.Substr(first, terminal), schema};
break;
case TableOrView::kView:
@@ -281,8 +292,9 @@
SqlSource::Rewriter rewriter(original_statement);
tokenizer_.Rewrite(rewriter, first_non_space_token, first, header,
SqliteTokenizer::EndToken::kExclusive);
- statement_ =
- CreateView{std::move(name), std::move(rewriter).Build(), schema};
+ statement_ = CreateView{replace, std::move(name),
+ tokenizer_.Substr(first, terminal),
+ std::move(rewriter).Build(), schema};
break;
}
statement_sql_ = tokenizer_.Substr(first_non_space_token, terminal);
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h
index b8120fb..012b55e 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.h
@@ -61,6 +61,7 @@
// Indicates that the specified SQL was a CREATE PERFETTO TABLE statement
// with the following parameters.
struct CreateTable {
+ bool replace;
std::string name;
// SQL source for the select statement.
SqlSource sql;
@@ -69,10 +70,13 @@
// Indicates that the specified SQL was a CREATE PERFETTO VIEW statement
// with the following parameters.
struct CreateView {
+ bool replace;
std::string name;
+ // SQL source for the select statement.
+ SqlSource select_sql;
// SQL source corresponding to the rewritten statement creating the
// underlying view.
- SqlSource sql;
+ SqlSource create_view_sql;
std::vector<sql_argument::ArgumentDefinition> schema;
};
// Indicates that the specified SQL was a INCLUDE PERFETTO MODULE statement
@@ -152,6 +156,7 @@
kView,
};
bool ParseCreatePerfettoTableOrView(
+ bool replace,
SqliteTokenizer::Token first_non_space_token,
TableOrView table_or_view);
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc
index 7856964..e3653ca 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser_unittest.cc
@@ -131,6 +131,14 @@
"STRING", FindSubstr(res, "select 'foo'"), false}));
}
+TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoFunctionScalar) {
+ auto res = SqlSource::FromExecuteQuery(
+ "create or replace perfetto function foo() returns INT as select 1");
+ ASSERT_THAT(*Parse(res), testing::ElementsAre(CreateFn{
+ true, FunctionPrototype{"foo", {}}, "INT",
+ FindSubstr(res, "select 1"), false}));
+}
+
TEST_F(PerfettoSqlParserTest, CreatePerfettoFunctionScalarError) {
auto res = SqlSource::FromExecuteQuery(
"create perfetto function foo( returns INT as select 1");
@@ -202,6 +210,19 @@
ASSERT_FALSE(parser.Next());
}
+TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoMacro) {
+ auto res = SqlSource::FromExecuteQuery(
+ "create or replace perfetto macro foo() returns Expr as 1");
+ PerfettoSqlParser parser(res, macros_);
+ ASSERT_TRUE(parser.Next());
+ ASSERT_EQ(parser.statement(), Statement(CreateMacro{true,
+ FindSubstr(res, "foo"),
+ {},
+ FindSubstr(res, "Expr"),
+ FindSubstr(res, "1")}));
+ ASSERT_FALSE(parser.Next());
+}
+
TEST_F(PerfettoSqlParserTest, CreatePerfettoMacroAndOther) {
auto res = SqlSource::FromExecuteQuery(
"create perfetto macro foo() returns sql1 as random sql snippet; "
@@ -226,9 +247,20 @@
"CREATE PERFETTO TABLE foo AS SELECT 42 AS bar");
PerfettoSqlParser parser(res, macros_);
ASSERT_TRUE(parser.Next());
- ASSERT_EQ(
- parser.statement(),
- Statement(CreateTable{"foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
+ ASSERT_EQ(parser.statement(),
+ Statement(CreateTable{
+ false, "foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
+ ASSERT_FALSE(parser.Next());
+}
+
+TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoTable) {
+ auto res = SqlSource::FromExecuteQuery(
+ "CREATE OR REPLACE PERFETTO TABLE foo AS SELECT 42 AS bar");
+ PerfettoSqlParser parser(res, macros_);
+ ASSERT_TRUE(parser.Next());
+ ASSERT_EQ(parser.statement(),
+ Statement(CreateTable{
+ true, "foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
ASSERT_FALSE(parser.Next());
}
@@ -238,6 +270,7 @@
PerfettoSqlParser parser(res, macros_);
ASSERT_TRUE(parser.Next());
ASSERT_EQ(parser.statement(), Statement(CreateTable{
+ false,
"foo",
FindSubstr(res, "SELECT 42 AS bar"),
{{"$bar", sql_argument::Type::kInt}},
@@ -250,9 +283,9 @@
"CREATE PERFETTO TABLE foo AS SELECT 42 AS bar; select 1");
PerfettoSqlParser parser(res, macros_);
ASSERT_TRUE(parser.Next());
- ASSERT_EQ(
- parser.statement(),
- Statement(CreateTable{"foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
+ ASSERT_EQ(parser.statement(),
+ Statement(CreateTable{
+ false, "foo", FindSubstr(res, "SELECT 42 AS bar"), {}}));
ASSERT_TRUE(parser.Next());
ASSERT_EQ(parser.statement(), Statement(SqliteSql{}));
ASSERT_EQ(parser.statement_sql(), FindSubstr(res, "select 1"));
@@ -264,11 +297,30 @@
"CREATE PERFETTO VIEW foo AS SELECT 42 AS bar");
PerfettoSqlParser parser(res, macros_);
ASSERT_TRUE(parser.Next());
- ASSERT_EQ(parser.statement(),
- Statement(CreateView{"foo",
- SqlSource::FromExecuteQuery(
- "CREATE VIEW foo AS SELECT 42 AS bar"),
- {}}));
+ ASSERT_EQ(
+ parser.statement(),
+ Statement(CreateView{
+ false,
+ "foo",
+ SqlSource::FromExecuteQuery("SELECT 42 AS bar"),
+ SqlSource::FromExecuteQuery("CREATE VIEW foo AS SELECT 42 AS bar"),
+ {}}));
+ ASSERT_FALSE(parser.Next());
+}
+
+TEST_F(PerfettoSqlParserTest, CreateOrReplacePerfettoView) {
+ auto res = SqlSource::FromExecuteQuery(
+ "CREATE OR REPLACE PERFETTO VIEW foo AS SELECT 42 AS bar");
+ PerfettoSqlParser parser(res, macros_);
+ ASSERT_TRUE(parser.Next());
+ ASSERT_EQ(
+ parser.statement(),
+ Statement(CreateView{
+ true,
+ "foo",
+ SqlSource::FromExecuteQuery("SELECT 42 AS bar"),
+ SqlSource::FromExecuteQuery("CREATE VIEW foo AS SELECT 42 AS bar"),
+ {}}));
ASSERT_FALSE(parser.Next());
}
@@ -277,11 +329,14 @@
"CREATE PERFETTO VIEW foo AS SELECT 42 AS bar; select 1");
PerfettoSqlParser parser(res, macros_);
ASSERT_TRUE(parser.Next());
- ASSERT_EQ(parser.statement(),
- Statement(CreateView{"foo",
- SqlSource::FromExecuteQuery(
- "CREATE VIEW foo AS SELECT 42 AS bar"),
- {}}));
+ ASSERT_EQ(
+ parser.statement(),
+ Statement(CreateView{
+ false,
+ "foo",
+ SqlSource::FromExecuteQuery("SELECT 42 AS bar"),
+ SqlSource::FromExecuteQuery("CREATE VIEW foo AS SELECT 42 AS bar"),
+ {}}));
ASSERT_TRUE(parser.Next());
ASSERT_EQ(parser.statement(), Statement(SqliteSql{}));
ASSERT_EQ(parser.statement_sql(), FindSubstr(res, "select 1"));
@@ -296,7 +351,9 @@
ASSERT_TRUE(parser.Next());
ASSERT_EQ(parser.statement(),
Statement(CreateView{
+ false,
"foo",
+ SqlSource::FromExecuteQuery("SELECT 'a' as foo, 42 AS bar"),
SqlSource::FromExecuteQuery(
"CREATE VIEW foo AS SELECT 'a' as foo, 42 AS bar"),
{{"$foo", sql_argument::Type::kString},
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_test_utils.h b/src/trace_processor/perfetto_sql/engine/perfetto_sql_test_utils.h
index 8382fe6..5a46950 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_test_utils.h
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_test_utils.h
@@ -46,7 +46,8 @@
inline bool operator==(const PerfettoSqlParser::CreateView& a,
const PerfettoSqlParser::CreateView& b) {
- return std::tie(a.name, a.sql) == std::tie(b.name, b.sql);
+ return std::tie(a.name, a.create_view_sql) ==
+ std::tie(b.name, b.create_view_sql);
}
inline bool operator==(const PerfettoSqlParser::Include& a,
@@ -82,7 +83,8 @@
}
if (auto* tab = std::get_if<PerfettoSqlParser::CreateView>(&line)) {
return stream << "CreateView(name=" << testing::PrintToString(tab->name)
- << ", sql=" << testing::PrintToString(tab->sql) << ")";
+ << ", sql=" << testing::PrintToString(tab->create_view_sql)
+ << ")";
}
if (auto* macro = std::get_if<PerfettoSqlParser::CreateMacro>(&line)) {
return stream << "CreateTable(name=" << testing::PrintToString(macro->name)
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/battery_stats.sql b/src/trace_processor/perfetto_sql/stdlib/android/battery_stats.sql
index 980003b..828400e 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/battery_stats.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/battery_stats.sql
@@ -132,10 +132,10 @@
) AS
SELECT
ts,
+ IFNULL(LEAD(ts) OVER (PARTITION BY name ORDER BY ts) - ts, -1) AS dur,
name AS track_name,
CAST(value AS INT64) AS value,
- android_battery_stats_counter_to_string(name, value) AS value_name,
- IFNULL(LEAD(ts) OVER (PARTITION BY name ORDER BY ts) - ts, -1) AS dur
+ android_battery_stats_counter_to_string(name, value) AS value_name
FROM counter
JOIN counter_track
ON counter.track_id = counter_track.id
@@ -166,10 +166,10 @@
dur INT,
-- The name of the counter track.
track_name STRING,
- -- The counter value as a number.
- value INT,
- -- The counter value as a human-readable string.
- value_name STRING
+ -- String value.
+ str_value STRING,
+ -- Int value.
+ int_value INT
) AS
WITH
event_markers AS (
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/binder.sql b/src/trace_processor/perfetto_sql/stdlib/android/binder.sql
index 7a5257b..8642fe2 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/binder.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/binder.sql
@@ -183,14 +183,14 @@
client_utid INT,
-- Tid of the client thread.
client_tid INT,
+ -- Pid of the client process.
+ client_pid INT,
+ -- Whether the txn was initiated from the main thread of the client process.
+ is_main_thread BOOL,
-- timestamp of the client txn.
client_ts INT,
-- dur of the client txn.
client_dur INT,
- -- oom score of the client process at the start of the txn.
- client_oom_score INT,
- -- Whether the txn was initiated from the main thread of the client process.
- is_main_thread BOOL,
-- slice id of the binder reply.
binder_reply_id INT,
-- name of the server process.
@@ -203,10 +203,14 @@
server_utid INT,
-- Tid of the server thread.
server_tid INT,
+ -- Pid of the server thread.
+ server_pid INT,
-- timestamp of the server txn.
server_ts INT,
-- dur of the server txn.
server_dur INT,
+ -- oom score of the client process at the start of the txn.
+ client_oom_score INT,
-- oom score of the server process at the start of the reply.
server_oom_score INT
) AS
@@ -249,8 +253,16 @@
CREATE PERFETTO VIEW android_sync_binder_thread_state_by_txn(
-- slice id of the binder txn
binder_txn_id INT,
+ -- Client timestamp
+ client_ts INT,
+ -- Client tid
+ client_tid INT,
-- slice id of the binder reply
binder_reply_id INT,
+ -- Server timestamp
+ server_ts INT,
+ -- Server tid
+ server_tid INT,
-- whether thread state is on the txn or reply side
thread_state_type STRING,
-- a thread_state that occurred in the txn
@@ -298,8 +310,16 @@
CREATE PERFETTO VIEW android_sync_binder_blocked_functions_by_txn(
-- slice id of the binder txn
binder_txn_id INT,
+ -- Client ts
+ client_ts INT,
+ -- Client tid
+ client_tid INT,
-- slice id of the binder reply
binder_reply_id INT,
+ -- Server ts
+ server_ts INT,
+ -- Server tid
+ server_tid INT,
-- whether thread state is on the txn or reply side
thread_state_type STRING,
-- blocked kernel function in a thread state
@@ -414,14 +434,14 @@
client_utid INT,
-- Tid of the client thread.
client_tid INT,
+ -- Pid of the client thread.
+ client_pid INT,
+ -- Whether the txn was initiated from the main thread of the client process.
+ is_main_thread BOOL,
-- timestamp of the client txn.
client_ts INT,
-- dur of the client txn.
client_dur INT,
- -- oom score of the client process at the start of the txn.
- client_oom_score INT,
- -- Whether the txn was initiated from the main thread of the client process.
- is_main_thread BOOL,
-- slice id of the binder reply.
binder_reply_id INT,
-- name of the server process.
@@ -434,10 +454,14 @@
server_utid INT,
-- Tid of the server thread.
server_tid INT,
+ -- Pid of the server thread.
+ server_pid INT,
-- timestamp of the server txn.
server_ts INT,
-- dur of the server txn.
server_dur INT,
+ -- oom score of the client process at the start of the txn.
+ client_oom_score INT,
-- oom score of the server process at the start of the reply.
server_oom_score INT
) AS
@@ -469,14 +493,14 @@
client_utid INT,
-- Tid of the client thread.
client_tid INT,
+ -- Pid of the client thread.
+ client_pid INT,
+ -- Whether the txn was initiated from the main thread of the client process.
+ is_main_thread BOOL,
-- timestamp of the client txn.
client_ts INT,
-- dur of the client txn.
client_dur INT,
- -- oom score of the client process at the start of the txn.
- client_oom_score INT,
- -- Whether the txn was initiated from the main thread of the client process.
- is_main_thread BOOL,
-- slice id of the binder reply.
binder_reply_id INT,
-- name of the server process.
@@ -489,10 +513,14 @@
server_utid INT,
-- Tid of the server thread.
server_tid INT,
+ -- Pid of the server thread.
+ server_pid INT,
-- timestamp of the server txn.
server_ts INT,
-- dur of the server txn.
server_dur INT,
+ -- oom score of the client process at the start of the txn.
+ client_oom_score INT,
-- oom score of the server process at the start of the reply.
server_oom_score INT,
-- whether the txn is synchronous or async (oneway).
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql b/src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql
index d7152b3..22fe345 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql
@@ -90,16 +90,14 @@
-- Aggregates dvfs counter slice for residency
CREATE PERFETTO VIEW android_dvfs_counter_residency(
- -- Id of the corresponding slice in slices table.
- slice_id INT,
- -- CPU that entered hypervisor.
- cpu INT,
- -- Timestamp when CPU entered hypervisor (in nanoseconds).
- ts INT,
- -- How much time CPU spent in hypervisor (in nanoseconds).
+ -- Counter name.
+ name STRING,
+ -- Counter value.
+ value INT,
+ -- Counter duration.
dur INT,
- -- Reason for entering hypervisor (e.g. host_hcall, host_mem_abort), or NULL if unknown.
- reason STRING
+ -- Counter duration as a percentage of total duration.
+ pct FLOAT
) AS
WITH
total AS (
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/io.sql b/src/trace_processor/perfetto_sql/stdlib/android/io.sql
index d4500fd..b5d7021 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/io.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/io.sql
@@ -16,19 +16,19 @@
-- Aggregates f2fs IO and latency stats by counter name.
CREATE PERFETTO VIEW android_io_f2fs_counter_stats(
-- Counter name on which all the other values are aggregated on.
- counter_name STRING,
+ name STRING,
-- Sum of all counter values for the counter name.
- counter_sum INT,
+ sum INT,
-- Max of all counter values for the counter name.
- counter_max INT,
+ max INT,
-- Min of all counter values for the counter name.
- counter_min INT,
+ min INT,
-- Duration between the first and last counter value for the counter name.
- counter_dur INT,
+ dur INT,
-- Count of all the counter values for the counter name.
- counter_count INT,
+ count INT,
-- Avergate of all the counter values for the counter name.
- counter_avg DOUBLE
+ avg DOUBLE
) AS
SELECT
STR_SPLIT(counter_track.name, '].', 1) AS name,
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql b/src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql
index 47cc758..6ed7232 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql
@@ -26,6 +26,10 @@
upid INT,
-- Process name.
process_name STRING,
+ -- Android app UID.
+ uid INT,
+ -- Whether the UID is shared by multiple packages.
+ shared_uid BOOL,
-- Name of the packages running in this process.
package_name STRING,
-- Package version code.
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/startup/BUILD.gn
index ee08f69..68c9bea 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/BUILD.gn
@@ -16,6 +16,7 @@
perfetto_sql_source_set("startup") {
sources = [
+ "internal_startup_events.sql",
"internal_startups_maxsdk28.sql",
"internal_startups_minsdk29.sql",
"internal_startups_minsdk33.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startup_events.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startup_events.sql
new file mode 100644
index 0000000..ed2e44d
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startup_events.sql
@@ -0,0 +1,28 @@
+--
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- All activity startup events.
+CREATE PERFETTO TABLE internal_startup_events AS
+SELECT
+ ts,
+ dur,
+ ts + dur AS ts_end,
+ STR_SPLIT(s.name, ": ", 1) AS package_name
+FROM slice s
+JOIN process_track t ON s.track_id = t.id
+JOIN process USING(upid)
+WHERE
+ s.name GLOB 'launching: *'
+ AND (process.name IS NULL OR process.name = 'system_server');
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_maxsdk28.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_maxsdk28.sql
index 2ca73f0..fa77966 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_maxsdk28.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_maxsdk28.sql
@@ -13,10 +13,11 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
+INCLUDE PERFETTO MODULE android.startup.internal_startup_events;
-INSERT INTO internal_all_startups
+CREATE PERFETTO TABLE internal_startups_maxsdk28 AS
SELECT
- "maxsdk28",
+ "maxsdk28" as sdk,
ROW_NUMBER() OVER(ORDER BY ts) AS startup_id,
le.ts,
le.ts_end AS ts_end,
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk29.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk29.sql
index 37611df..e9536d1 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk29.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk29.sql
@@ -13,6 +13,8 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
+INCLUDE PERFETTO MODULE android.startup.internal_startup_events;
+
-- Marks the beginning of the trace and is equivalent to when the statsd startup
-- logging begins.
CREATE PERFETTO VIEW internal_activity_intent_received AS
@@ -48,9 +50,9 @@
-- Use the starting event package name. The finish event package name
-- is not reliable in the case of failed startups.
-INSERT INTO internal_all_startups
+CREATE PERFETTO TABLE internal_startups_minsdk29 AS
SELECT
- "minsdk29",
+ "minsdk29" as sdk,
lpart.startup_id,
lpart.ts,
le.ts_end,
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk33.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk33.sql
index 9bb1937..118dda6 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk33.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk33.sql
@@ -13,12 +13,13 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
+INCLUDE PERFETTO MODULE android.startup.internal_startup_events;
CREATE PERFETTO VIEW internal_startup_async_events AS
SELECT
ts,
dur,
- SUBSTR(name, 19) AS startup_id
+ CAST(SUBSTR(name, 19) AS INT) AS startup_id
FROM slice
WHERE
name GLOB 'launchingActivity#*'
@@ -27,7 +28,7 @@
CREATE PERFETTO VIEW internal_startup_complete_events AS
SELECT
- STR_SPLIT(completed, ':', 0) AS startup_id,
+ CAST(STR_SPLIT(completed, ':', 0) AS INT) AS startup_id,
STR_SPLIT(completed, ':', 2) AS package_name,
CASE
WHEN STR_SPLIT(completed, ':', 1) = 'completed-hot' THEN 'hot'
@@ -47,14 +48,14 @@
)
GROUP BY 1, 2, 3;
-INSERT INTO internal_all_startups
+CREATE PERFETTO TABLE internal_startups_minsdk33 AS
SELECT
- "minsdk33",
+ "minsdk33" as sdk,
startup_id,
ts,
ts + dur AS ts_end,
dur,
- package_name,
+ package_name AS package,
startup_type
FROM internal_startup_async_events
JOIN internal_startup_complete_events USING (startup_id);
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups.sql
index 97959d7..fa55876 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups.sql
@@ -15,36 +15,18 @@
INCLUDE PERFETTO MODULE common.slices;
INCLUDE PERFETTO MODULE android.process_metadata;
-
--- All activity startup events.
-CREATE PERFETTO TABLE internal_startup_events AS
-SELECT
- ts,
- dur,
- ts + dur AS ts_end,
- STR_SPLIT(s.name, ": ", 1) AS package_name
-FROM slice s
-JOIN process_track t ON s.track_id = t.id
-JOIN process USING(upid)
-WHERE
- s.name GLOB 'launching: *'
- AND (process.name IS NULL OR process.name = 'system_server');
-
--- Gather all startup data. Populate by different sdks.
-CREATE TABLE internal_all_startups(
- sdk STRING,
- startup_id INTEGER BIGINT,
- ts BIGINT,
- ts_end BIGINT,
- dur BIGINT,
- package STRING,
- startup_type STRING
-);
-
INCLUDE PERFETTO MODULE android.startup.internal_startups_maxsdk28;
INCLUDE PERFETTO MODULE android.startup.internal_startups_minsdk29;
INCLUDE PERFETTO MODULE android.startup.internal_startups_minsdk33;
+-- Gather all startup data. Populate by different sdks.
+CREATE PERFETTO TABLE internal_all_startups AS
+SELECT sdk, startup_id, ts, ts_end, dur, package, startup_type FROM internal_startups_maxsdk28
+UNION ALL
+SELECT sdk, startup_id, ts, ts_end, dur, package, startup_type FROM internal_startups_minsdk29
+UNION ALL
+SELECT sdk, startup_id, ts, ts_end, dur, package, startup_type FROM internal_startups_minsdk33;
+
-- All activity startups in the trace by startup id.
-- Populated by different scripts depending on the platform version/contents.
CREATE PERFETTO TABLE android_startups(
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scrolls.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scrolls.sql
index b03751d..80b371c 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scrolls.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scrolls.sql
@@ -81,6 +81,8 @@
CREATE PERFETTO VIEW chrome_scrolling_intervals(
-- The unique identifier of the scroll interval. This may span multiple scrolls if they overlap.
id INT,
+ -- Comma-separated list of scroll ids that are included in this interval.
+ scroll_ids STRING,
-- The start timestamp of the scroll interval.
ts INT,
-- The duration of the scroll interval.
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql
index a890e70..4fa04a7 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql
@@ -119,22 +119,24 @@
-- Unique id for the thread that ran within the slice.
utid INT,
-- The CPU's power state before this slice.
- previous_power_state INT
+ previous_power_state INT,
+ -- A unique ID for the CPU power-up.
+ powerup_id INT
) AS
- SELECT
- ts,
- dur,
- cpu,
- id AS sched_id,
- utid,
- previous_power_state,
- powerup_id
- FROM internal_cpu_power_and_sched_slice
- WHERE power_state = 0 -- Power-ups only.
- GROUP BY cpu, powerup_id
- HAVING ts = MIN(ts) -- There will only be one MIN sched slice
- -- per CPU power up.
- ORDER BY ts ASC;
+SELECT
+ ts,
+ dur,
+ cpu,
+ id AS sched_id,
+ utid,
+ previous_power_state,
+ powerup_id
+FROM internal_cpu_power_and_sched_slice
+WHERE power_state = 0 -- Power-ups only.
+GROUP BY cpu, powerup_id
+HAVING ts = MIN(ts) -- There will only be one MIN sched slice
+ -- per CPU power up.
+ORDER BY ts ASC;
-- A view joining thread tracks and top-level slices.
--
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql
index 97afa3f..35f933a 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/page_loads.sql
@@ -88,7 +88,7 @@
fcp.dur AS fcp,
fcp.ts + fcp.dur AS fcp_ts,
lcp.dur AS lcp,
- IFNULL(lcp.dur, 0) + IFNULL(lcp.ts, 0) AS lcp_ts,
+ lcp.dur + lcp.ts AS lcp_ts,
load_fired.ts AS dom_content_loaded_event_ts,
start_load.ts AS load_event_ts,
timing_loaded.ts AS mark_fully_loaded_ts,
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_intervals.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_intervals.sql
index 27186ab..e73cdab 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_intervals.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_intervals.sql
@@ -87,10 +87,10 @@
CREATE PERFETTO VIEW chrome_scroll_stats(
-- Id of the individual scroll.
scroll_id INT,
- -- The number of missed vsyncs in the scroll.
- missed_vsyncs INT,
-- The number of frames in the scroll.
frame_count INT,
+ -- The number of missed vsyncs in the scroll.
+ missed_vsyncs INT,
-- The number presented frames in the scroll.
presented_frame_count INT,
-- The number of janky frames in the scroll.
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_v3.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_v3.sql
index d27c862..303b8c5 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_v3.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_v3.sql
@@ -276,12 +276,14 @@
CREATE PERFETTO VIEW chrome_frame_info_with_delay(
-- gesture scroll slice id.
id INT,
- -- OS timestamp of the first touch move arrival within a frame.
- min_start_ts INT,
-- OS timestamp of the last touch move arrival within a frame.
max_start_ts INT,
+ -- OS timestamp of the first touch move arrival within a frame.
+ min_start_ts INT,
-- The scroll which the touch belongs to.
scroll_id INT,
+ -- ID of the associated scroll update.
+ scroll_update_id INT,
-- Trace ids of all frames presented in at this vsync.
encapsulated_scroll_ids INT,
-- Summation of all delta_y of all gesture scrolls in this frame.
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_offsets.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_offsets.sql
index bfa4580..d9d817a 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_offsets.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_offsets.sql
@@ -107,11 +107,11 @@
-- All of the presented frame scroll update ids.
CREATE PERFETTO VIEW chrome_deltas_presented_frame_scroll_update_ids(
- -- ID slice of the presented frame.
- arg_set_id INT,
-- A scroll update id that was included in the presented frame.
-- There may be zero, one, or more.
- scroll_update_id INT
+ scroll_update_id INT,
+ -- Slice id
+ id INT
) AS
SELECT
args.int_value AS scroll_update_id,
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/tasks.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/tasks.sql
index 054940f..bda37f5 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/tasks.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/tasks.sql
@@ -268,13 +268,15 @@
-- Whether this slice is a part of non-accelerated capture toolbar screenshot.
is_software_screenshot BOOL,
-- Whether this slice is a part of accelerated capture toolbar screenshot.
- is_hardware_screenshot BOOL
+ is_hardware_screenshot BOOL,
+ -- Slice id.
+ slice_id INT
) AS
SELECT
java_view.name AS filtered_name,
java_view.is_software_screenshot,
java_view.is_hardware_screenshot,
- slice.*
+ slice.id as slice_id
FROM internal_chrome_java_views java_view
JOIN slice USING (id);
@@ -373,6 +375,8 @@
CREATE PERFETTO VIEW chrome_scheduler_tasks(
-- Slice id.
id INT,
+ -- Type.
+ type STRING,
-- Name of the task.
name STRING,
-- Timestamp.
@@ -389,6 +393,8 @@
process_name STRING,
-- Same as slice.track_id.
track_id INT,
+ -- Same as slice.category.
+ category STRING,
-- Same as slice.depth.
depth INT,
-- Same as slice.parent_id.
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/args.sql b/src/trace_processor/perfetto_sql/stdlib/common/args.sql
index 5fc4836..df0615a 100644
--- a/src/trace_processor/perfetto_sql/stdlib/common/args.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/common/args.sql
@@ -22,10 +22,10 @@
-- Id of the arg set.
arg_set_id INT,
-- Key of the argument.
- key STRING
+ arg_key STRING
)
-- Formatted value of the argument.
RETURNS STRING AS
SELECT display_value
FROM args
-WHERE arg_set_id = $arg_set_id AND key = $key;
\ No newline at end of file
+WHERE arg_set_id = $arg_set_id AND key = $arg_key;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql b/src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql
index 7c5cf9a..7ec8afe 100644
--- a/src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql
@@ -16,12 +16,12 @@
INCLUDE PERFETTO MODULE common.counters;
INCLUDE PERFETTO MODULE common.timestamps;
-CREATE PERFETTO FUNCTION internal_number_generator(to INT)
+CREATE PERFETTO FUNCTION internal_number_generator(upper_limit INT)
RETURNS TABLE(num INT) AS
WITH nums AS
(SELECT 1 num UNION SELECT num + 1
from NUMS
- WHERE num < $to)
+ WHERE num < $upper_limit)
SELECT num FROM nums;
--
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/slices.sql b/src/trace_processor/perfetto_sql/stdlib/common/slices.sql
index e9c326d..36176c0 100644
--- a/src/trace_processor/perfetto_sql/stdlib/common/slices.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/common/slices.sql
@@ -57,6 +57,7 @@
) AS
SELECT
slice.id,
+ slice.type,
slice.ts,
slice.dur,
slice.category,
diff --git a/src/trace_processor/perfetto_sql/stdlib/experimental/proto_path.sql b/src/trace_processor/perfetto_sql/stdlib/experimental/proto_path.sql
index 06be141..5ab538c 100644
--- a/src/trace_processor/perfetto_sql/stdlib/experimental/proto_path.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/experimental/proto_path.sql
@@ -15,8 +15,8 @@
-- Creates a Stack consisting of one frame for a path in the
-- EXPERIMENTAL_PROTO_PATH table.
-CREATE PERFETTO FUNCTON EXPERIMENTAL_PROTO_PATH_TO_FRAME(
- -- Id of the path in EXPERIMENTAL_PROTO_PATH.
+CREATE PERFETTO FUNCTION experimental_proto_path_to_frame(
+-- Id of the path in EXPERIMENTAL_PROTO_PATH.
path_id LONG)
-- Stack with one frame
RETURNS BYTES AS
diff --git a/src/trace_processor/sqlite/sqlite_engine.cc b/src/trace_processor/sqlite/sqlite_engine.cc
index 23b064a..2a76c53 100644
--- a/src/trace_processor/sqlite/sqlite_engine.cc
+++ b/src/trace_processor/sqlite/sqlite_engine.cc
@@ -169,6 +169,17 @@
return base::OkStatus();
}
+base::Status SqliteEngine::UnregisterFunction(const char* name, int argc) {
+ int ret = sqlite3_create_function_v2(db_.get(), name, static_cast<int>(argc),
+ SQLITE_UTF8, nullptr, nullptr, nullptr,
+ nullptr, nullptr);
+ if (ret != SQLITE_OK) {
+ return base::ErrStatus("Unable to unregister function with name %s", name);
+ }
+ fn_ctx_.Erase({name, argc});
+ return base::OkStatus();
+}
+
base::Status SqliteEngine::DeclareVirtualTable(const std::string& create_stmt) {
int res = sqlite3_declare_vtab(db_.get(), create_stmt.c_str());
if (res != SQLITE_OK) {
diff --git a/src/trace_processor/sqlite/sqlite_engine.h b/src/trace_processor/sqlite/sqlite_engine.h
index 1a1b089..42ef5ae 100644
--- a/src/trace_processor/sqlite/sqlite_engine.h
+++ b/src/trace_processor/sqlite/sqlite_engine.h
@@ -89,6 +89,9 @@
FnCtxDestructor* ctx_destructor,
bool deterministic);
+ // Unregisters a C++ function from SQL.
+ base::Status UnregisterFunction(const char* name, int argc);
+
// Registers a SQLite virtual table module with the given name.
template <typename Vtab, typename Context>
void RegisterVirtualTableModule(const std::string& module_name,
diff --git a/src/trace_processor/tables/profiler_tables.py b/src/trace_processor/tables/profiler_tables.py
index 8361d8c..dfcc93e 100644
--- a/src/trace_processor/tables/profiler_tables.py
+++ b/src/trace_processor/tables/profiler_tables.py
@@ -161,7 +161,7 @@
C('name', CppString()),
C('mapping', CppTableId(STACK_PROFILE_MAPPING_TABLE)),
C('rel_pc', CppInt64()),
- C('symbol_set_id', CppOptional(CppUint32())),
+ C('symbol_set_id', CppOptional(CppUint32()), flags=ColumnFlag.DENSE),
C('deobfuscated_name', CppOptional(CppString())),
],
tabledoc=TableDoc(
diff --git a/src/trace_processor/util/sql_modules.h b/src/trace_processor/util/sql_modules.h
index 4a52e7d..997acb0 100644
--- a/src/trace_processor/util/sql_modules.h
+++ b/src/trace_processor/util/sql_modules.h
@@ -22,6 +22,7 @@
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/basic_types.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index c02af04..a6bf239 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -220,14 +220,14 @@
bool first_batch_in_cycle,
CompactSchedBuffer* compact_sched_buf,
const std::set<FtraceDataSource*>& started_data_sources) {
+ const auto sys_page_size = base::GetSysPageSize();
size_t pages_read = 0;
{
metatrace::ScopedEvent evt(metatrace::TAG_FTRACE,
metatrace::FTRACE_CPU_READ_BATCH);
for (; pages_read < max_pages;) {
- uint8_t* curr_page = parsing_buf + (pages_read * base::kPageSize);
- ssize_t res =
- PERFETTO_EINTR(read(*trace_fd_, curr_page, base::kPageSize));
+ uint8_t* curr_page = parsing_buf + (pages_read * sys_page_size);
+ ssize_t res = PERFETTO_EINTR(read(*trace_fd_, curr_page, sys_page_size));
if (res < 0) {
// Expected errors:
// EAGAIN: no data (since we're in non-blocking mode).
@@ -253,7 +253,7 @@
PERFETTO_DLOG("[cpu%zu]: 0-sized read from ftrace pipe.", cpu_);
break;
}
- PERFETTO_CHECK(res == static_cast<ssize_t>(base::kPageSize));
+ PERFETTO_CHECK(res == static_cast<ssize_t>(sys_page_size));
pages_read += 1;
@@ -267,11 +267,11 @@
// fragmentation, i.e. for the fact that the last trace event didn't fit
// in the current page and hence the current page was terminated
// prematurely.
- static constexpr size_t kRoughlyAPage = base::kPageSize - 512;
+ static const size_t kRoughlyAPage = sys_page_size - 512;
const uint8_t* scratch_ptr = curr_page;
std::optional<PageHeader> hdr =
ParsePageHeader(&scratch_ptr, table_->page_header_size_len());
- PERFETTO_DCHECK(hdr && hdr->size > 0 && hdr->size <= base::kPageSize);
+ PERFETTO_DCHECK(hdr && hdr->size > 0 && hdr->size <= sys_page_size);
if (!hdr.has_value()) {
PERFETTO_ELOG("[cpu%zu]: can't parse page header", cpu_);
break;
@@ -305,8 +305,8 @@
if (pages_parsed_ok != pages_read) {
const size_t first_bad_page_idx = pages_parsed_ok;
const uint8_t* curr_page =
- parsing_buf + (first_bad_page_idx * base::kPageSize);
- LogInvalidPage(curr_page, base::kPageSize);
+ parsing_buf + (first_bad_page_idx * sys_page_size);
+ LogInvalidPage(curr_page, sys_page_size);
PERFETTO_FATAL("Failed to parse ftrace page");
}
}
@@ -423,8 +423,9 @@
size_t pages_parsed = 0;
bool compact_sched_enabled = ds_config->compact_sched.enabled;
for (; pages_parsed < pages_read; pages_parsed++) {
- const uint8_t* curr_page = parsing_buf + (pages_parsed * base::kPageSize);
- const uint8_t* curr_page_end = curr_page + base::kPageSize;
+ const uint8_t* curr_page =
+ parsing_buf + (pages_parsed * base::GetSysPageSize());
+ const uint8_t* curr_page_end = curr_page + base::GetSysPageSize();
const uint8_t* parse_pos = curr_page;
std::optional<PageHeader> page_header =
ParsePageHeader(&parse_pos, table->page_header_size_len());
@@ -489,7 +490,7 @@
// (clearing the bit internally).
constexpr static uint64_t kMissedEventsFlag = (1ull << 31);
- const uint8_t* end_of_page = *ptr + base::kPageSize;
+ const uint8_t* end_of_page = *ptr + base::GetSysPageSize();
PageHeader page_header;
if (!CpuReader::ReadAndAdvance<uint64_t>(ptr, end_of_page,
&page_header.timestamp))
@@ -505,7 +506,7 @@
page_header.size = size_and_flags & kDataSizeMask;
page_header.lost_events = bool(size_and_flags & kMissedEventsFlag);
- PERFETTO_DCHECK(page_header.size <= base::kPageSize);
+ PERFETTO_DCHECK(page_header.size <= base::GetSysPageSize());
// Reject rest of the number, if applicable. On 32-bit, size_bytes - 4 will
// evaluate to 0 and this will be a no-op. On 64-bit, this will advance by 4
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index 6fc629d..474c10a 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -64,7 +64,7 @@
void AllocateIfNeeded() {
// PagedMemory stays valid as long as it was allocated once.
if (!ftrace_data_.IsValid()) {
- ftrace_data_ = base::PagedMemory::Allocate(base::kPageSize *
+ ftrace_data_ = base::PagedMemory::Allocate(base::GetSysPageSize() *
kFtraceDataBufSizePages);
}
// Heap-allocated buffer gets freed and reallocated.
@@ -96,7 +96,7 @@
}
size_t ftrace_data_buf_pages() const {
PERFETTO_DCHECK(ftrace_data_.size() ==
- base::kPageSize * kFtraceDataBufSizePages);
+ base::GetSysPageSize() * kFtraceDataBufSizePages);
return kFtraceDataBufSizePages;
}
CompactSchedBuffer* compact_sched_buf() const {
diff --git a/src/traced/probes/ftrace/cpu_reader_benchmark.cc b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
index c7db2a1..c9c9062 100644
--- a/src/traced/probes/ftrace/cpu_reader_benchmark.cc
+++ b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
@@ -934,11 +934,12 @@
ProtoTranslationTable* table = GetTable(test_case.name);
auto repeated_pages =
- std::make_unique<uint8_t[]>(base::kPageSize * page_repetition);
+ std::make_unique<uint8_t[]>(base::GetSysPageSize() * page_repetition);
{
auto page = PageFromXxd(test_case.data);
for (size_t i = 0; i < page_repetition; i++) {
- memcpy(&repeated_pages[i * base::kPageSize], &page[0], base::kPageSize);
+ memcpy(&repeated_pages[i * base::GetSysPageSize()], &page[0],
+ base::GetSysPageSize());
}
}
diff --git a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
index 002fd53..0559f02 100644
--- a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
+++ b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
@@ -31,15 +31,9 @@
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
namespace perfetto {
-namespace {
-
-uint8_t g_page[base::kPageSize];
-
-} // namespace
using perfetto::protos::pbzero::FtraceEventBundle;
-void FuzzCpuReaderParsePage(const uint8_t* data, size_t size);
void FuzzCpuReaderProcessPagesForDataSource(const uint8_t* data, size_t size);
// TODO(rsavitski): make the fuzzer generate multi-page payloads.
@@ -50,8 +44,10 @@
"Could not read table. "
"This fuzzer must be run in the root directory.");
}
- memset(g_page, 0, base::kPageSize);
- memcpy(g_page, data, std::min(base::kPageSize, size));
+
+ static uint8_t* g_page = new uint8_t[base::GetSysPageSize()];
+ memset(g_page, 0, base::GetSysPageSize());
+ memcpy(g_page, data, std::min(size_t(base::GetSysPageSize()), size));
FtraceMetadata metadata{};
FtraceDataSourceConfig ds_config{/*event_filter=*/EventFilter{},
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 1e69e3d..25fc580 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -164,7 +164,9 @@
class BinaryWriter {
public:
BinaryWriter()
- : size_(base::kPageSize), page_(new uint8_t[size_]), ptr_(page_.get()) {}
+ : size_(base::GetSysPageSize()),
+ page_(new uint8_t[size_]),
+ ptr_(page_.get()) {}
template <typename T>
void Write(T t) {
@@ -443,7 +445,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_EQ(44ul, page_header->size);
EXPECT_FALSE(page_header->lost_events);
@@ -566,7 +568,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -609,7 +611,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -653,7 +655,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -745,7 +747,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -791,7 +793,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
ASSERT_FALSE(page_header->lost_events);
ASSERT_LE(parse_pos + page_header->size, page_end);
@@ -867,10 +869,12 @@
// Prepare a buffer with 8 contiguous pages, with the above contents.
static constexpr size_t kTestPages = 8;
- std::unique_ptr<uint8_t[]> buf(new uint8_t[base::kPageSize * kTestPages]());
+ std::unique_ptr<uint8_t[]> buf(
+ new uint8_t[base::GetSysPageSize() * kTestPages]());
for (size_t i = 0; i < kTestPages; i++) {
- void* dest = buf.get() + (i * base::kPageSize);
- memcpy(dest, static_cast<const void*>(test_page_order[i]), base::kPageSize);
+ void* dest = buf.get() + (i * base::GetSysPageSize());
+ memcpy(dest, static_cast<const void*>(test_page_order[i]),
+ base::GetSysPageSize());
}
auto compact_sched_buf = std::make_unique<CompactSchedBuffer>();
@@ -988,7 +992,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -1037,7 +1041,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -1152,7 +1156,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -1351,7 +1355,7 @@
InvalidCompactSchedEventFormatForTesting(), printk_formats);
FtraceDataSourceConfig ds_config = EmptyConfig();
- FakeEventProvider provider(base::kPageSize);
+ FakeEventProvider provider(base::GetSysPageSize());
BinaryWriter writer;
@@ -1437,7 +1441,7 @@
auto input = writer.GetCopy();
auto length = writer.written();
- BundleProvider bundle_provider(base::kPageSize);
+ BundleProvider bundle_provider(base::GetSysPageSize());
FtraceMetadata metadata{};
ASSERT_TRUE(CpuReader::ParseEvent(
@@ -1486,7 +1490,7 @@
auto input = writer.GetCopy();
auto length = writer.written();
- BundleProvider bundle_provider(base::kPageSize);
+ BundleProvider bundle_provider(base::GetSysPageSize());
FtraceMetadata metadata{};
ASSERT_TRUE(CpuReader::ParseEvent(
@@ -1504,7 +1508,7 @@
}
TEST(CpuReaderTest, TaskRenameEvent) {
- BundleProvider bundle_provider(base::kPageSize);
+ BundleProvider bundle_provider(base::GetSysPageSize());
BinaryWriter writer;
ProtoTranslationTable* table = GetTable("android_seed_N2F62_3.10.49");
@@ -1536,7 +1540,7 @@
// zero-terminated strings in some cases. Even though it's a kernel bug, there's
// no point in rejecting that.
TEST(CpuReaderTest, EventNonZeroTerminated) {
- BundleProvider bundle_provider(base::kPageSize);
+ BundleProvider bundle_provider(base::GetSysPageSize());
BinaryWriter writer;
ProtoTranslationTable* table = GetTable("android_seed_N2F62_3.10.49");
@@ -1612,10 +1616,12 @@
// Prepare a buffer with 8 contiguous pages, with the above contents.
static constexpr size_t kTestPages = 8;
- std::unique_ptr<uint8_t[]> buf(new uint8_t[base::kPageSize * kTestPages]());
+ std::unique_ptr<uint8_t[]> buf(
+ new uint8_t[base::GetSysPageSize() * kTestPages]());
for (size_t i = 0; i < kTestPages; i++) {
- void* dest = buf.get() + (i * base::kPageSize);
- memcpy(dest, static_cast<const void*>(test_page_order[i]), base::kPageSize);
+ void* dest = buf.get() + (i * base::GetSysPageSize());
+ memcpy(dest, static_cast<const void*>(test_page_order[i]),
+ base::GetSysPageSize());
}
ProtoTranslationTable* table = GetTable("synthetic");
@@ -1662,10 +1668,12 @@
// Prepare a buffer with 8 contiguous pages, with the above contents.
static constexpr size_t kTestPages = 8;
- std::unique_ptr<uint8_t[]> buf(new uint8_t[base::kPageSize * kTestPages]());
+ std::unique_ptr<uint8_t[]> buf(
+ new uint8_t[base::GetSysPageSize() * kTestPages]());
for (size_t i = 0; i < kTestPages; i++) {
- void* dest = buf.get() + (i * base::kPageSize);
- memcpy(dest, static_cast<const void*>(test_page_order[i]), base::kPageSize);
+ void* dest = buf.get() + (i * base::GetSysPageSize());
+ memcpy(dest, static_cast<const void*>(test_page_order[i]),
+ base::GetSysPageSize());
}
ProtoTranslationTable* table = GetTable("synthetic");
@@ -1881,7 +1889,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -2365,7 +2373,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -2888,7 +2896,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_TRUE(page_header->lost_events); // data loss
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -2994,7 +3002,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -3296,7 +3304,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -3341,7 +3349,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
@@ -3395,7 +3403,7 @@
std::optional<CpuReader::PageHeader> page_header =
CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
- const uint8_t* page_end = page.get() + base::kPageSize;
+ const uint8_t* page_end = page.get() + base::GetSysPageSize();
ASSERT_TRUE(page_header.has_value());
EXPECT_FALSE(page_header->lost_events);
EXPECT_LE(parse_pos + page_header->size, page_end);
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index 0be1842..4d6019e 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -574,7 +574,7 @@
requested_buffer_size_kb = kMaxPerCpuBufferSizeKb;
}
- size_t pages = requested_buffer_size_kb / (base::kPageSize / 1024);
+ size_t pages = requested_buffer_size_kb / (base::GetSysPageSize() / 1024);
if (pages == 0)
return 1;
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index 8e83c57..835488f 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -19,6 +19,7 @@
#include <memory>
#include "ftrace_config_muxer.h"
+#include "perfetto/ext/base/utils.h"
#include "src/traced/probes/ftrace/atrace_wrapper.h"
#include "src/traced/probes/ftrace/compact_sched.h"
#include "src/traced/probes/ftrace/ftrace_procfs.h"
@@ -53,6 +54,10 @@
"sys_read",
};
+std::string PageSizeKb() {
+ return std::to_string(base::GetSysPageSize() / 1024);
+}
+
class MockFtraceProcfs : public FtraceProcfs {
public:
MockFtraceProcfs() : FtraceProcfs("/root/") {
@@ -229,17 +234,22 @@
};
TEST_F(FtraceConfigMuxerTest, ComputeCpuBufferSizeInPages) {
- static constexpr size_t kMaxBufSizeInPages = 16 * 1024u;
- // No buffer size given: good default (128 pages = 2mb).
- EXPECT_EQ(ComputeCpuBufferSizeInPages(0), 512u);
+ auto Pages = [](uint32_t kb) { return kb * 1024 / base::GetSysPageSize(); };
+ const size_t kMaxBufSizeInPages = Pages(64 * 1024);
+
+ // No buffer size given: good default (2mb).
+ EXPECT_EQ(ComputeCpuBufferSizeInPages(0), Pages(2048));
+
// Buffer size given way too big: good default.
EXPECT_EQ(ComputeCpuBufferSizeInPages(10 * 1024 * 1024), kMaxBufSizeInPages);
- // The limit is 64mb per CPU, 512mb is too much.
EXPECT_EQ(ComputeCpuBufferSizeInPages(512 * 1024), kMaxBufSizeInPages);
+
// Your size ends up with less than 1 page per cpu -> 1 page.
EXPECT_EQ(ComputeCpuBufferSizeInPages(3), 1u);
+
// You picked a good size -> your size rounded to nearest page.
- EXPECT_EQ(ComputeCpuBufferSizeInPages(42), 10u);
+ EXPECT_EQ(ComputeCpuBufferSizeInPages(42),
+ 42 * 1024 / base::GetSysPageSize());
}
TEST_F(FtraceConfigMuxerTest, GenericSyscallFiltering) {
@@ -613,7 +623,7 @@
EXPECT_CALL(ftrace,
WriteToFile("/root/events/sched/sched_switch/enable", "0"));
EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
- EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
+ EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", PageSizeKb()));
EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
EXPECT_CALL(ftrace, ClearFile("/root/trace"));
EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
@@ -1128,7 +1138,7 @@
EXPECT_CALL(ftrace, AppendToFile("/root/set_event", "!cgroup:cgroup_mkdir"))
.WillOnce(Return(true));
EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
- EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
+ EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", PageSizeKb()));
EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
EXPECT_CALL(ftrace, ClearFile("/root/trace"));
EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index 5ffc6c2..c9a5ca1 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -58,6 +58,10 @@
constexpr char kFooEnablePath[] = "/root/events/group/foo/enable";
constexpr char kBarEnablePath[] = "/root/events/group/bar/enable";
+std::string PageSizeKb() {
+ return std::to_string(base::GetSysPageSize() / 1024);
+}
+
class MockTaskRunner : public base::TaskRunner {
public:
MOCK_METHOD(void, PostTask, (std::function<void()>), (override));
@@ -346,7 +350,8 @@
// State clearing on tracing teardown.
EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
- EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
+ EXPECT_CALL(*controller->procfs(),
+ WriteToFile("/root/buffer_size_kb", PageSizeKb()));
EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
.WillOnce(Return(true));
@@ -401,7 +406,8 @@
EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
EXPECT_CALL(*controller->procfs(), WriteToFile(kBarEnablePath, "0"));
EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
- EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
+ EXPECT_CALL(*controller->procfs(),
+ WriteToFile("/root/buffer_size_kb", PageSizeKb()));
EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
.WillOnce(Return(true));
@@ -435,7 +441,8 @@
// State clearing on tracing teardown.
EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
- EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
+ EXPECT_CALL(*controller->procfs(),
+ WriteToFile("/root/buffer_size_kb", PageSizeKb()));
EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
.WillOnce(Return(true));
@@ -456,7 +463,8 @@
// Every time a fake data source is destroyed, the controller will reset the
// buffer size to a single page.
- EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"))
+ EXPECT_CALL(*controller->procfs(),
+ WriteToFile("/root/buffer_size_kb", PageSizeKb()))
.Times(AnyNumber());
{
@@ -501,9 +509,9 @@
{
// You picked a good size -> your size rounded to nearest page.
EXPECT_CALL(*controller->procfs(),
- WriteToFile("/root/buffer_size_kb", "40"));
+ WriteToFile("/root/buffer_size_kb", "64"));
FtraceConfig config = CreateFtraceConfig({"group/foo"});
- config.set_buffer_size_kb(42);
+ config.set_buffer_size_kb(65);
auto data_source = controller->AddFakeDataSource(config);
ASSERT_TRUE(controller->StartDataSource(data_source.get()));
}
@@ -511,10 +519,10 @@
{
// You picked a good size -> your size rounded to nearest page.
EXPECT_CALL(*controller->procfs(),
- WriteToFile("/root/buffer_size_kb", "40"));
+ WriteToFile("/root/buffer_size_kb", "64"));
FtraceConfig config = CreateFtraceConfig({"group/foo"});
ON_CALL(*controller->procfs(), NumberOfCpus()).WillByDefault(Return(2));
- config.set_buffer_size_kb(42);
+ config.set_buffer_size_kb(65);
auto data_source = controller->AddFakeDataSource(config);
ASSERT_TRUE(controller->StartDataSource(data_source.get()));
}
@@ -681,8 +689,9 @@
EXPECT_CALL(
*controller->GetInstanceMockProcfs("secondary"),
WriteToFile("/root/instances/secondary/events/group/foo/enable", "0"));
- EXPECT_CALL(*controller->GetInstanceMockProcfs("secondary"),
- WriteToFile("/root/instances/secondary/buffer_size_kb", "4"));
+ EXPECT_CALL(
+ *controller->GetInstanceMockProcfs("secondary"),
+ WriteToFile("/root/instances/secondary/buffer_size_kb", PageSizeKb()));
controller->RemoveDataSource(data_source.get());
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 396ba60..f092468 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -415,12 +415,12 @@
}
bool FtraceProcfs::SetCpuBufferSizeInPages(size_t pages) {
- if (pages * base::kPageSize > 1 * 1024 * 1024 * 1024) {
+ if (pages * base::GetSysPageSize() > 1 * 1024 * 1024 * 1024) {
PERFETTO_ELOG("Tried to set the per CPU buffer size to more than 1gb.");
return false;
}
std::string path = root_ + "buffer_size_kb";
- return WriteNumberToFile(path, pages * (base::kPageSize / 1024ul));
+ return WriteNumberToFile(path, pages * (base::GetSysPageSize() / 1024ul));
}
bool FtraceProcfs::GetTracingOn() {
diff --git a/src/traced/probes/ftrace/test/cpu_reader_support.cc b/src/traced/probes/ftrace/test/cpu_reader_support.cc
index 020298b..dc386ff 100644
--- a/src/traced/probes/ftrace/test/cpu_reader_support.cc
+++ b/src/traced/probes/ftrace/test/cpu_reader_support.cc
@@ -54,9 +54,9 @@
}
std::unique_ptr<uint8_t[]> PageFromXxd(const std::string& text) {
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[base::kPageSize]);
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[base::GetSysPageSize()]);
const char* ptr = text.data();
- memset(buffer.get(), 0xfa, base::kPageSize);
+ memset(buffer.get(), 0xfa, base::GetSysPageSize());
uint8_t* out = buffer.get();
while (*ptr != '\0') {
if (*(ptr++) != ':')
diff --git a/src/tracing/core/trace_buffer_unittest.cc b/src/tracing/core/trace_buffer_unittest.cc
index 6fc36f6..b98ecf0 100644
--- a/src/tracing/core/trace_buffer_unittest.cc
+++ b/src/tracing/core/trace_buffer_unittest.cc
@@ -1992,16 +1992,16 @@
TEST_F(TraceBufferTest, Clone_CommitOnlyUsedSize) {
const size_t kPages = 32;
- const size_t kPageSize = base::GetSysPageSize();
- ResetBuffer(kPageSize * kPages);
+ const size_t page_size = base::GetSysPageSize();
+ ResetBuffer(page_size * kPages);
CreateChunk(ProducerID(1), WriterID(0), ChunkID(0))
.AddPacket(1024, static_cast<char>('a'))
.CopyIntoTraceBuffer();
using base::vm_test_utils::IsMapped;
auto is_only_first_page_mapped = [&](const TraceBuffer& buf) {
- bool first_mapped = IsMapped(GetBufData(buf), kPageSize);
- bool rest_mapped = IsMapped(GetBufData(buf) + kPageSize, kPages - 1);
+ bool first_mapped = IsMapped(GetBufData(buf), page_size);
+ bool rest_mapped = IsMapped(GetBufData(buf) + page_size, kPages - 1);
return first_mapped && !rest_mapped;
};
diff --git a/test/data/chrome_example_wikipedia.perfetto_trace.gz.sha256 b/test/data/chrome_example_wikipedia.perfetto_trace.gz.sha256
new file mode 100644
index 0000000..a3da3d9
--- /dev/null
+++ b/test/data/chrome_example_wikipedia.perfetto_trace.gz.sha256
@@ -0,0 +1 @@
+7401f67e30cb025b113cef1db53d7e154705e688e33142f65fd7875df26f2cf0
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index fbf8a66..28c5abd 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -87,31 +87,38 @@
from diff_tests.parser.translated_args.tests import TranslatedArgs
from diff_tests.parser.ufs.tests import Ufs
from diff_tests.stdlib.android.tests import AndroidStdlib
-from diff_tests.stdlib.common.tests import StdlibCommon
from diff_tests.stdlib.chrome.tests import ChromeStdlib
from diff_tests.stdlib.chrome.tests_chrome_interactions import ChromeInteractions
from diff_tests.stdlib.chrome.tests_scroll_jank import ChromeScrollJankStdlib
from diff_tests.stdlib.common.tests import StdlibCommon
+from diff_tests.stdlib.common.tests import StdlibCommon
from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
+from diff_tests.stdlib.intervals.tests import StdlibIntervals
from diff_tests.stdlib.linux.tests import LinuxStdlib
from diff_tests.stdlib.pkvm.tests import Pkvm
-from diff_tests.stdlib.intervals.tests import StdlibIntervals
+from diff_tests.stdlib.prelude.math_functions_tests import PreludeMathFunctions
+from diff_tests.stdlib.prelude.pprof_functions_tests import PreludePprofFunctions
+from diff_tests.stdlib.prelude.window_functions_tests import PreludeWindowFunctions
from diff_tests.stdlib.sched.tests import StdlibSched
from diff_tests.stdlib.slices.tests import Slices
from diff_tests.stdlib.span_join.tests_left_join import SpanJoinLeftJoin
from diff_tests.stdlib.span_join.tests_outer_join import SpanJoinOuterJoin
from diff_tests.stdlib.span_join.tests_regression import SpanJoinRegression
from diff_tests.stdlib.span_join.tests_smoke import SpanJoinSmoke
+from diff_tests.stdlib.tests import StdlibSmoke
from diff_tests.stdlib.timestamps.tests import Timestamps
-from diff_tests.syntax.functions.tests import Functions
-from diff_tests.syntax.perfetto_sql.tests import PerfettoSql
+from diff_tests.syntax.function_tests import PerfettoFunction
+from diff_tests.syntax.include_tests import PerfettoInclude
+from diff_tests.syntax.macro_tests import PerfettoMacro
+from diff_tests.syntax.table_function_tests import PerfettoTableFunction
+from diff_tests.syntax.table_tests import PerfettoTable
+from diff_tests.syntax.view_tests import PerfettoView
from diff_tests.tables.tests import Tables
from diff_tests.tables.tests_counters import TablesCounters
from diff_tests.tables.tests_sched import TablesSched
sys.path.pop()
-
def fetch_all_diff_tests(index_path: str) -> List['testing.TestCase']:
parser_tests = [
*AndroidBugreport(index_path, 'parser/android',
@@ -218,7 +225,15 @@
*DynamicTables(index_path, 'stdlib/dynamic_tables',
'DynamicTables').fetch(),
*LinuxStdlib(index_path, 'stdlib/linux', 'LinuxStdlib').fetch(),
+ *PreludeMathFunctions(index_path, 'stdlib/prelude',
+ 'PreludeMathFunctions').fetch(),
+ *PreludePprofFunctions(index_path, 'stdlib/prelude',
+ 'PreludePprofFunctions').fetch(),
+ *PreludeWindowFunctions(index_path, 'stdlib/prelude',
+ 'PreludeWindowFunctions').fetch(),
*Pkvm(index_path, 'stdlib/pkvm', 'Pkvm').fetch(),
+ *StdlibSmoke(index_path, 'stdlib', 'StdlibSmoke').fetch(),
+ *StdlibCommon(index_path, 'stdlib/common', 'StdlibCommon').fetch(),
*Slices(index_path, 'stdlib/slices', 'Slices').fetch(),
*SpanJoinLeftJoin(index_path, 'stdlib/span_join',
'SpanJoinLeftJoin').fetch(),
@@ -234,8 +249,13 @@
]
syntax_tests = [
- *Functions(index_path, 'syntax/functions', 'Functions').fetch(),
- *PerfettoSql(index_path, 'syntax/perfetto_sql', 'PerfettoSql').fetch(),
+ *PerfettoFunction(index_path, 'syntax', 'PerfettoFunction').fetch(),
+ *PerfettoInclude(index_path, 'syntax', 'PerfettoInclude').fetch(),
+ *PerfettoMacro(index_path, 'syntax', 'PerfettoMacro').fetch(),
+ *PerfettoTable(index_path, 'syntax', 'PerfettoTable').fetch(),
+ *PerfettoTableFunction(index_path, 'syntax',
+ 'PerfettoTableFunction').fetch(),
+ *PerfettoView(index_path, 'syntax', 'PerfettoView').fetch(),
]
return parser_tests + metrics_tests + stdlib_tests + syntax_tests + [
diff --git a/test/trace_processor/diff_tests/stdlib/android/android_battery_stats_state.out b/test/trace_processor/diff_tests/stdlib/android/android_battery_stats_state.out
deleted file mode 100644
index eb16850..0000000
--- a/test/trace_processor/diff_tests/stdlib/android/android_battery_stats_state.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"ts","track_name","value","value_name","dur"
-1000,"battery_stats.audio",1,"active",-1
-1000,"battery_stats.data_conn",13,"4G (LTE)",3000
-4000,"battery_stats.data_conn",20,"5G (NR)",-1
diff --git a/test/trace_processor/diff_tests/stdlib/android/tests.py b/test/trace_processor/diff_tests/stdlib/android/tests.py
index 56bcf3a..3f2800f 100644
--- a/test/trace_processor/diff_tests/stdlib/android/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/android/tests.py
@@ -105,7 +105,12 @@
SELECT * FROM android_battery_stats_state
ORDER BY ts, track_name;
""",
- out=Path('android_battery_stats_state.out'))
+ out=Csv("""
+ "ts","dur","track_name","value","value_name"
+ 1000,-1,"battery_stats.audio",1,"active"
+ 1000,3000,"battery_stats.data_conn",13,"4G (LTE)"
+ 4000,-1,"battery_stats.data_conn",20,"5G (NR)"
+ """))
def test_anrs(self):
return DiffTestBlueprint(
@@ -842,8 +847,8 @@
"""))
def test_android_dvfs_counters(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
packet {
ftrace_events {
cpu: 0
@@ -876,11 +881,11 @@
trusted_packet_sequence_id: 2
}
"""),
- query="""
+ query="""
INCLUDE PERFETTO MODULE android.dvfs;
SELECT * FROM android_dvfs_counters;
""",
- out=Csv("""
+ out=Csv("""
"name","ts","value","dur"
"domain@1 Frequency",200001000000,400000.000000,2000000
"domain@1 Frequency",200003000000,1024000.000000,2000000
@@ -888,8 +893,8 @@
"""))
def test_android_dvfs_counter_stats(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
packet {
ftrace_events {
cpu: 0
@@ -946,19 +951,19 @@
trusted_packet_sequence_id: 2
}
"""),
- query="""
+ query="""
INCLUDE PERFETTO MODULE android.dvfs;
SELECT * FROM android_dvfs_counter_stats;
""",
- out=Csv("""
+ out=Csv("""
"name","max","min","dur","wgt_avg"
"bus_throughput Frequency",1014000.000000,553000.000000,4000000,783499.942375
"domain@1 Frequency",1024000.000000,400000.000000,4000000,712000.078000
"""))
def test_android_dvfs_counter_residency(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
packet {
ftrace_events {
cpu: 0
@@ -991,11 +996,11 @@
trusted_packet_sequence_id: 2
}
"""),
- query="""
+ query="""
INCLUDE PERFETTO MODULE android.dvfs;
SELECT * FROM android_dvfs_counter_residency;
""",
- out=Csv("""
+ out=Csv("""
"name","value","dur","pct"
"bus_throughput Frequency",553000.000000,2000000,50.000000
"bus_throughput Frequency",1014000.000000,2000000,50.000000
diff --git a/test/trace_processor/diff_tests/stdlib/chrome/tests_chrome_interactions.py b/test/trace_processor/diff_tests/stdlib/chrome/tests_chrome_interactions.py
index 5019e54..473c38c 100644
--- a/test/trace_processor/diff_tests/stdlib/chrome/tests_chrome_interactions.py
+++ b/test/trace_processor/diff_tests/stdlib/chrome/tests_chrome_interactions.py
@@ -46,5 +46,5 @@
10,687434796215243,475000000,687435271215243,475000000,687435271215243,1
11,687435970742243,763000000,687436733742243,852000000,687436822742243,1
13,687438343638243,1005000000,687439348638243,1005000000,687439348638243,1
- 14,687440258111243,900000000,687441158111243,"[NULL]",0,1
+ 14,687440258111243,900000000,687441158111243,"[NULL]","[NULL]",1
"""))
diff --git a/test/trace_processor/diff_tests/stdlib/common/tests.py b/test/trace_processor/diff_tests/stdlib/common/tests.py
index a902b5b..8e85918 100644
--- a/test/trace_processor/diff_tests/stdlib/common/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/common/tests.py
@@ -45,4 +45,116 @@
"state","cpu","dur"
"Running",1,50
"Runnable","[NULL]",25
- """))
\ No newline at end of file
+ """))
+
+ def test_spans_overlapping_dur_intersect_edge(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(0, 2, 1, 2) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 1
+ """))
+
+ def test_spans_overlapping_dur_intersect_edge_reversed(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(1, 2, 0, 2) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 1
+ """))
+
+ def test_spans_overlapping_dur_intersect_all(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(0, 3, 1, 1) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 1
+ """))
+
+ def test_spans_overlapping_dur_intersect_all_reversed(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(1, 1, 0, 3) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 1
+ """))
+
+ def test_spans_overlapping_dur_no_intersect(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(0, 1, 2, 1) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 0
+ """))
+
+ def test_spans_overlapping_dur_no_intersect_reversed(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(2, 1, 0, 1) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 0
+ """))
+
+ def test_spans_overlapping_dur_negative_dur(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(0, -1, 0, 1) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 0
+ """))
+
+ def test_spans_overlapping_dur_negative_dur_reversed(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT SPANS_OVERLAPPING_DUR(0, 1, 0, -1) AS dur
+ """,
+ out=Csv("""
+ "dur"
+ 0
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/prelude/math_functions_tests.py b/test/trace_processor/diff_tests/stdlib/prelude/math_functions_tests.py
new file mode 100644
index 0000000..edf9877
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/prelude/math_functions_tests.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from python.generators.diff_tests.testing import PrintProfileProto
+from google.protobuf import text_format
+
+
+class PreludeMathFunctions(TestSuite):
+
+ def test_math_ln_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT
+ CAST(LN(1) * 1000 AS INTEGER) AS valid,
+ LN("as") AS invalid_str,
+ LN(NULL) AS invalid_null
+ """,
+ out=Csv("""
+ "valid","invalid_str","invalid_null"
+ 0,"[NULL]","[NULL]"
+ """))
+
+ def test_math_exp_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT
+ CAST(EXP(1) * 1000 AS INTEGER) AS valid,
+ EXP("asd") AS invalid_str,
+ EXP(NULL) AS invalid_null
+ """,
+ out=Csv("""
+ "valid","invalid_str","invalid_null"
+ 2718,"[NULL]","[NULL]"
+ """))
+
+ def test_math_sqrt_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT
+ CAST(SQRT(4) AS INTEGER) AS valid,
+ SQRT("asd") AS invalid_str,
+ SQRT(NULL) AS invalid_null
+ """,
+ out=Csv("""
+ "valid","invalid_str","invalid_null"
+ 2,"[NULL]","[NULL]"
+ """))
+
+ def test_math_functions(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT
+ CAST(SQRT(EXP(LN(1))) AS INTEGER) AS valid,
+ SQRT(EXP(LN("asd"))) AS invalid_str,
+ SQRT(EXP(LN(NULL))) AS invalid_null
+ """,
+ out=Csv("""
+ "valid","invalid_str","invalid_null"
+ 1,"[NULL]","[NULL]"
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/prelude/pprof_functions_tests.py b/test/trace_processor/diff_tests/stdlib/prelude/pprof_functions_tests.py
new file mode 100644
index 0000000..d810c08
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/prelude/pprof_functions_tests.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from python.generators.diff_tests.testing import PrintProfileProto
+from google.protobuf import text_format
+
+
+class PreludePprofFunctions(TestSuite):
+
+ def test_stacks(self):
+ return DiffTestBlueprint(
+ trace=DataPath("perf_sample.pb"),
+ query="""
+ SELECT HEX(
+ CAT_STACKS(
+ "A",
+ CAT_STACKS(
+ "B",
+ CAT_STACKS(
+ "C",
+ STACK_FROM_STACK_PROFILE_CALLSITE(5),
+ "D")
+ ),
+ "E",
+ NULL,
+ STACK_FROM_STACK_PROFILE_CALLSITE(14),
+ STACK_FROM_STACK_PROFILE_CALLSITE(NULL),
+ STACK_FROM_STACK_PROFILE_FRAME(4),
+ STACK_FROM_STACK_PROFILE_FRAME(NULL)))
+ """,
+ out=BinaryProto(
+ message_type="perfetto.protos.Stack",
+ contents="""
+ entries {
+ frame_id: 4
+ }
+ entries {
+ callsite_id: 14
+ }
+ entries {
+ name: "E"
+ }
+ entries {
+ name: "D"
+ }
+ entries {
+ callsite_id: 5
+ }
+ entries {
+ name: "C"
+ }
+ entries {
+ name: "B"
+ }
+ entries {
+ name: "A"
+ }
+ """))
+
+ def test_profile_no_functions(self):
+ return DiffTestBlueprint(
+ trace=DataPath("perf_sample_no_functions.pb"),
+ query="""
+ SELECT HEX(
+ EXPERIMENTAL_PROFILE(STACK_FROM_STACK_PROFILE_CALLSITE(callsite_id))
+ )
+ FROM PERF_SAMPLE
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 1
+ Stack:
+ (0x7a4167d3f8)
+ (0x783153c8e4)
+ (0x7a4161ef8c)
+ (0x7a42c3d8b0)
+ (0x7a4167d9f4)
+ (0x7a4163bc44)
+ (0x7a4172f330)
+ (0x7a4177a658)
+ (0x7a4162b3a0)
+
+ Sample:
+ Values: 1
+ Stack:
+ (0x7a4167d9f8)
+ (0x7a4163bc44)
+ (0x7a4172f330)
+ (0x7a4177a658)
+ (0x7a4162b3a0)
+ """))
+
+ def test_profile_default_sample_types(self):
+ return DiffTestBlueprint(
+ trace=DataPath("perf_sample.pb"),
+ query="""
+ SELECT HEX(
+ EXPERIMENTAL_PROFILE(
+ CAT_STACKS(
+ "A",
+ STACK_FROM_STACK_PROFILE_CALLSITE(2),
+ "B"
+ )))
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 1
+ Stack:
+ B (0x0)
+ perfetto::base::UnixTaskRunner::Run() (0x7a4172f330)
+ perfetto::ServiceMain(int, char**) (0x7a4177a658)
+ __libc_init (0x7a4162b3a0)
+ A (0x0)
+ """))
+
+ def test_profile_with_sample_types(self):
+ return DiffTestBlueprint(
+ trace=DataPath("perf_sample.pb"),
+ query="""
+ SELECT HEX(
+ EXPERIMENTAL_PROFILE(
+ CAT_STACKS("A", "B"), "type", "units", 42))
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 42
+ Stack:
+ B (0x0)
+ A (0x0)
+ """))
+
+ def test_profile_aggregates_samples(self):
+ return DiffTestBlueprint(
+ trace=DataPath("perf_sample.pb"),
+ query="""
+ WITH samples(stack, value) AS (
+ VALUES
+ (CAT_STACKS("A", "B"), 4),
+ (CAT_STACKS("A", "B"), 8),
+ (CAT_STACKS("A", "B"), 15),
+ (CAT_STACKS("A", "C"), 16),
+ (CAT_STACKS("C", "B"), 23),
+ (CAT_STACKS("C", "B"), 42)
+ )
+ SELECT HEX(
+ EXPERIMENTAL_PROFILE(
+ stack, "type", "units", value))
+ FROM samples
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 16
+ Stack:
+ C (0x0)
+ A (0x0)
+
+ Sample:
+ Values: 27
+ Stack:
+ B (0x0)
+ A (0x0)
+
+ Sample:
+ Values: 65
+ Stack:
+ B (0x0)
+ C (0x0)
+ """))
+
+ def test_annotated_callstack(self):
+ return DiffTestBlueprint(
+ trace=DataPath("perf_sample_annotations.pftrace"),
+ query="""
+ SELECT HEX(EXPERIMENTAL_PROFILE(STACK_FROM_STACK_PROFILE_CALLSITE(251, TRUE)))
+ """,
+ out=BinaryProto(
+ message_type="perfetto.third_party.perftools.profiles.Profile",
+ post_processing=PrintProfileProto,
+ contents="""
+ Sample:
+ Values: 1
+ Stack:
+ art::ResolveFieldWithAccessChecks(art::Thread*, art::ClassLinker*, unsigned short, art::ArtMethod*, bool, bool, unsigned long) [common-frame] (0x724da79a74)
+ NterpGetInstanceFieldOffset [common-frame-interp] (0x724da794b0)
+ nterp_get_instance_field_offset [common-frame-interp] (0x724dcfc070)
+ nterp_op_iget_object_slow_path [common-frame-interp] (0x724dcf5884)
+ android.view.ViewRootImpl.notifyDrawStarted [interp] (0x7248f894d2)
+ android.view.ViewRootImpl.performTraversals [aot] (0x71b8d378)
+ android.view.ViewRootImpl.doTraversal [aot] (0x71b93220)
+ android.view.ViewRootImpl$TraversalRunnable.run [aot] (0x71ab0384)
+ android.view.Choreographer.doCallbacks [aot] (0x71a91b6c)
+ android.view.Choreographer.doFrame [aot] (0x71a92550)
+ android.view.Choreographer$FrameDisplayEventReceiver.run [aot] (0x71b26fb0)
+ android.os.Handler.dispatchMessage [aot] (0x71975924)
+ android.os.Looper.loopOnce [aot] (0x71978d6c)
+ android.os.Looper.loop [aot] (0x719788a0)
+ android.app.ActivityThread.main [aot] (0x717454cc)
+ art_quick_invoke_static_stub [common-frame] (0x724db2de00)
+ _jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long) [common-frame] (0x724db545ec)
+ art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*) (.__uniq.165753521025965369065708152063621506277) (0x724db53ad0)
+ art_jni_trampoline [common-frame] (0x6ff5c578)
+ com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run [aot] (0x71c4ab6c)
+ com.android.internal.os.ZygoteInit.main [aot] (0x71c54c7c)
+ art_quick_invoke_static_stub (0x724db2de00)
+ art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list) (0x724dc422a8)
+ art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list) (0x724dcc57c8)
+ _JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...) (0x74e1b03ca8)
+ android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool) (0x74e1b0feac)
+ main (0x63da9c354c)
+ __libc_init (0x74ff4a0728)
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/prelude/window_functions_tests.py b/test/trace_processor/diff_tests/stdlib/prelude/window_functions_tests.py
new file mode 100644
index 0000000..2840394
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/prelude/window_functions_tests.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from python.generators.diff_tests.testing import PrintProfileProto
+from google.protobuf import text_format
+
+
+class PreludeWindowFunctions(TestSuite):
+
+ def test_first_non_null_frame(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ CREATE TABLE TEST(id INTEGER, val INTEGER);
+
+ INSERT INTO TEST
+ VALUES (1, 1), (2, NULL), (3, 3), (4, 4), (5, NULL), (6, NULL), (7, NULL);
+
+ SELECT
+ id,
+ LAST_NON_NULL(val)
+ OVER (ORDER BY id ASC ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS val
+ FROM TEST
+ ORDER BY id ASC;
+ """,
+ out=Csv("""
+ "id","val"
+ 1,3
+ 2,4
+ 3,4
+ 4,4
+ 5,"[NULL]"
+ 6,"[NULL]"
+ 7,"[NULL]"
+ """))
+
+ def test_first_non_null_partition(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ CREATE TABLE TEST(id INTEGER, part TEXT, val INTEGER);
+
+ INSERT INTO TEST
+ VALUES
+ (1, 'A', 1),
+ (2, 'A', NULL),
+ (3, 'A', 3),
+ (4, 'B', NULL),
+ (5, 'B', 5),
+ (6, 'B', NULL),
+ (7, 'B', 7);
+
+ SELECT id, LAST_NON_NULL(val) OVER (PARTITION BY part ORDER BY id ASC) AS val
+ FROM TEST
+ ORDER BY id ASC;
+ """,
+ out=Csv("""
+ "id","val"
+ 1,1
+ 2,1
+ 3,3
+ 4,"[NULL]"
+ 5,5
+ 6,5
+ 7,7
+ """))
+
+ def test_first_non_null(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+
+ """),
+ query="""
+ CREATE TABLE TEST(id INTEGER, val INTEGER);
+
+ INSERT INTO TEST
+ VALUES (1, 1), (2, NULL), (3, 3), (4, 4), (5, NULL), (6, NULL), (7, NULL);
+
+ SELECT id, LAST_NON_NULL(val) OVER (ORDER BY id ASC) AS val
+ FROM TEST
+ ORDER BY id ASC;
+ """,
+ out=Csv("""
+ "id","val"
+ 1,1
+ 2,1
+ 3,3
+ 4,4
+ 5,4
+ 6,4
+ 7,4
+ """))
+
+ def test_layout(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ """),
+ query="""
+ CREATE TABLE TEST(start INTEGER, end INTEGER);
+
+ INSERT INTO TEST
+ VALUES
+ (1, 5),
+ (2, 4),
+ (3, 8),
+ (6, 7),
+ (6, 7),
+ (6, 7);
+
+ WITH custom_slices as (
+ SELECT
+ start as ts,
+ end - start as dur
+ FROM test
+ )
+ SELECT
+ ts,
+ INTERNAL_LAYOUT(ts, dur) over (
+ order by ts
+ rows between unbounded preceding and current row
+ ) as depth
+ FROM custom_slices
+ """,
+ out=Csv("""
+ "ts","depth"
+ 1,0
+ 2,1
+ 3,2
+ 6,0
+ 6,1
+ 6,3
+ """))
+
+ def test_layout_with_instant_events(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ """),
+ query="""
+ CREATE TABLE TEST(start INTEGER, end INTEGER);
+
+ INSERT INTO TEST
+ VALUES
+ (1, 5),
+ (2, 2),
+ (3, 3),
+ (4, 4);
+
+ WITH custom_slices as (
+ SELECT
+ start as ts,
+ end - start as dur
+ FROM test
+ )
+ SELECT
+ ts,
+ INTERNAL_LAYOUT(ts, dur) over (
+ order by ts
+ rows between unbounded preceding and current row
+ ) as depth
+ FROM custom_slices
+ """,
+ out=Csv("""
+ "ts","depth"
+ 1,0
+ 2,1
+ 3,1
+ 4,1
+ """))
+
+ def test_layout_with_events_without_end(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ """),
+ query="""
+ CREATE TABLE TEST(ts INTEGER, dur INTEGER);
+
+ INSERT INTO TEST
+ VALUES
+ (1, -1),
+ (2, -1),
+ (3, 5),
+ (4, 1),
+ (5, 1);
+
+ SELECT
+ ts,
+ INTERNAL_LAYOUT(ts, dur) over (
+ order by ts
+ rows between unbounded preceding and current row
+ ) as depth
+ FROM test
+ """,
+ out=Csv("""
+ "ts","depth"
+ 1,0
+ 2,1
+ 3,2
+ 4,3
+ 5,3
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/tests.py b/test/trace_processor/diff_tests/stdlib/tests.py
new file mode 100644
index 0000000..5f2ca20
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/tests.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import 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
+
+
+# Smoke tests for the entire stdlib. These tests load a trace and then import the entire stdlib to check that it's well-formed.
+class StdlibSmoke(TestSuite):
+
+ def test_empty_file(self):
+ return DiffTestBlueprint(
+ trace=TextProto(''),
+ query="""
+ INCLUDE PERFETTO MODULE *;
+
+ SELECT 1 as result;
+ """,
+ out=Csv("""
+ "result"
+ 1
+ """))
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/syntax/function_tests.py b/test/trace_processor/diff_tests/syntax/function_tests.py
new file mode 100644
index 0000000..6c656ba
--- /dev/null
+++ b/test/trace_processor/diff_tests/syntax/function_tests.py
@@ -0,0 +1,296 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from python.generators.diff_tests.testing import PrintProfileProto
+from google.protobuf import text_format
+
+
+class PerfettoFunction(TestSuite):
+
+ def test_create_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ CREATE PERFETTO FUNCTION f(x INT) RETURNS INT AS SELECT $x + 1;
+
+ SELECT f(5) as result;
+ """,
+ out=Csv("""
+ "result"
+ 6
+ """))
+
+ def test_replace_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ CREATE PERFETTO FUNCTION f(x INT) RETURNS INT AS SELECT $x + 1;
+ CREATE OR REPLACE PERFETTO FUNCTION f(x INT) RETURNS INT AS SELECT $x + 2;
+
+ SELECT f(5) as result;
+ """,
+ out=Csv("""
+ "result"
+ 7
+ """))
+
+ def test_legacy_create_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT create_function('f(x INT)', 'INT', 'SELECT $x + 1');
+
+ SELECT f(5) as result;
+ """,
+ out=Csv("""
+ "result"
+ 6
+ """))
+
+ def test_legacy_create_function_returns_string(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT create_function('f(x INT)', 'STRING', 'SELECT "value_" || $x');
+
+ SELECT f(5) as result;
+ """,
+ out=Csv("""
+ "result"
+ "value_5"
+ """))
+
+ def test_legacy_create_function_duplicated(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT create_function('f()', 'INT', 'SELECT 1');
+ SELECT create_function('f()', 'INT', 'SELECT 1');
+
+ SELECT f() as result;
+ """,
+ out=Csv("""
+ "result"
+ 1
+ """))
+
+ def test_legacy_create_function_recursive(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ -- Compute factorial.
+ SELECT create_function('f(x INT)', 'INT',
+ '
+ SELECT IIF($x = 0, 1, $x * f($x - 1))
+ ');
+
+ SELECT f(5) as result;
+ """,
+ out=Csv("""
+ "result"
+ 120
+ """))
+
+ def test_legacy_create_function_recursive_string(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ -- Compute factorial.
+ SELECT create_function('f(x INT)', 'STRING',
+ '
+ SELECT IIF(
+ $x = 0,
+ "",
+ -- 97 is the ASCII code for "a".
+ f($x - 1) || char(96 + $x) || f($x - 1))
+ ');
+
+ SELECT f(4) as result;
+ """,
+ out=Csv("""
+ "result"
+ "abacabadabacaba"
+ """))
+
+ def test_legacy_create_function_recursive_string_memoized(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ -- Compute factorial.
+ SELECT create_function('f(x INT)', 'STRING',
+ '
+ SELECT IIF(
+ $x = 0,
+ "",
+ -- 97 is the ASCII code for "a".
+ f($x - 1) || char(96 + $x) || f($x - 1))
+ ');
+
+ SELECT experimental_memoize('f');
+
+ SELECT f(4) as result;
+ """,
+ out=Csv("""
+ "result"
+ "abacabadabacaba"
+ """))
+
+ def test_legacy_create_function_memoize(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ -- Compute 2^n inefficiently to test memoization.
+ -- If it times out, memoization is not working.
+ SELECT create_function('f(x INT)', 'INT',
+ '
+ SELECT IIF($x = 0, 1, f($x - 1) + f($x - 1))
+ ');
+
+ SELECT EXPERIMENTAL_MEMOIZE('f');
+
+ -- 2^50 is too expensive to compute, but memoization makes it fast.
+ SELECT f(50) as result;
+ """,
+ out=Csv("""
+ "result"
+ 1125899906842624
+ """))
+
+ def test_legacy_create_function_memoize_float(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ -- Compute 2^n inefficiently to test memoization.
+ -- If it times out, memoization is not working.
+ SELECT create_function('f(x INT)', 'FLOAT',
+ '
+ SELECT $x + 0.5
+ ');
+
+ SELECT EXPERIMENTAL_MEMOIZE('f');
+
+ SELECT printf("%.1f", f(1)) as result
+ UNION ALL
+ SELECT printf("%.1f", f(1)) as result
+ UNION ALL
+ SELECT printf("%.1f", f(1)) as result
+ """,
+ out=Csv("""
+ "result"
+ "1.5"
+ "1.5"
+ "1.5"
+ """))
+
+ def test_legacy_create_function_memoize_intermittent_memoization(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ -- This function returns NULL for odd numbers and 1 for even numbers.
+ -- As we do not memoize NULL results, we would only memoize the results
+ -- for even numbers.
+ SELECT create_function('f(x INT)', 'INT',
+ '
+ SELECT IIF($x = 0, 1,
+ IIF(f($x - 1) IS NULL, 1, NULL)
+ )
+ ');
+
+ SELECT EXPERIMENTAL_MEMOIZE('f');
+
+ SELECT
+ f(50) as f_50,
+ f(51) as f_51;
+ """,
+ out=Csv("""
+ "f_50","f_51"
+ 1,"[NULL]"
+ """))
+
+ def test_legacy_create_function_memoize_subtree_size(self):
+ # Tree:
+ # 1
+ # / \
+ # / \
+ # / \
+ # 2 3
+ # / \ / \
+ # 4 5 6 7
+ # / \ | | | \
+ # 8 9 10 11 12 13
+ # | |
+ # 14 15
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ CREATE PERFETTO TABLE tree AS
+ WITH data(id, parent_id) as (VALUES
+ (1, NULL),
+ (2, 1),
+ (3, 1),
+ (4, 2),
+ (5, 2),
+ (6, 3),
+ (7, 3),
+ (8, 4),
+ (9, 4),
+ (10, 5),
+ (11, 6),
+ (12, 7),
+ (13, 7),
+ (14, 8),
+ (15, 9)
+ )
+ SELECT * FROM data;
+
+ SELECT create_function('subtree_size(id INT)', 'INT',
+ '
+ SELECT 1 + IFNULL((
+ SELECT
+ SUM(subtree_size(child.id))
+ FROM tree child
+ WHERE child.parent_id = $id
+ ), 0)
+ ');
+
+ SELECT EXPERIMENTAL_MEMOIZE('subtree_size');
+
+ SELECT
+ id, subtree_size(id) as size
+ FROM tree
+ ORDER BY id;
+ """,
+ out=Csv("""
+ "id","size"
+ 1,15
+ 2,8
+ 3,6
+ 4,5
+ 5,2
+ 6,2
+ 7,3
+ 8,2
+ 9,2
+ 10,1
+ 11,1
+ 12,1
+ 13,1
+ 14,1
+ 15,1
+ """))
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/syntax/functions/tests.py b/test/trace_processor/diff_tests/syntax/functions/tests.py
deleted file mode 100644
index f613fb5..0000000
--- a/test/trace_processor/diff_tests/syntax/functions/tests.py
+++ /dev/null
@@ -1,879 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import TestSuite
-from python.generators.diff_tests.testing import PrintProfileProto
-from google.protobuf import text_format
-
-
-class Functions(TestSuite):
-
- def test_create_function(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT create_function('f(x INT)', 'INT', 'SELECT $x + 1');
-
- SELECT f(5) as result;
- """,
- out=Csv("""
- "result"
- 6
- """))
-
- def test_create_function_returns_string(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT create_function('f(x INT)', 'STRING', 'SELECT "value_" || $x');
-
- SELECT f(5) as result;
- """,
- out=Csv("""
- "result"
- "value_5"
- """))
-
- def test_create_function_duplicated(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT create_function('f()', 'INT', 'SELECT 1');
- SELECT create_function('f()', 'INT', 'SELECT 1');
-
- SELECT f() as result;
- """,
- out=Csv("""
- "result"
- 1
- """))
-
- def test_create_function_recursive(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- -- Compute factorial.
- SELECT create_function('f(x INT)', 'INT',
- '
- SELECT IIF($x = 0, 1, $x * f($x - 1))
- ');
-
- SELECT f(5) as result;
- """,
- out=Csv("""
- "result"
- 120
- """))
-
- def test_create_function_recursive_string(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- -- Compute factorial.
- SELECT create_function('f(x INT)', 'STRING',
- '
- SELECT IIF(
- $x = 0,
- "",
- -- 97 is the ASCII code for "a".
- f($x - 1) || char(96 + $x) || f($x - 1))
- ');
-
- SELECT f(4) as result;
- """,
- out=Csv("""
- "result"
- "abacabadabacaba"
- """))
-
- def test_create_function_recursive_string_memoized(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- -- Compute factorial.
- SELECT create_function('f(x INT)', 'STRING',
- '
- SELECT IIF(
- $x = 0,
- "",
- -- 97 is the ASCII code for "a".
- f($x - 1) || char(96 + $x) || f($x - 1))
- ');
-
- SELECT experimental_memoize('f');
-
- SELECT f(4) as result;
- """,
- out=Csv("""
- "result"
- "abacabadabacaba"
- """))
-
- def test_create_function_memoize(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- -- Compute 2^n inefficiently to test memoization.
- -- If it times out, memoization is not working.
- SELECT create_function('f(x INT)', 'INT',
- '
- SELECT IIF($x = 0, 1, f($x - 1) + f($x - 1))
- ');
-
- SELECT EXPERIMENTAL_MEMOIZE('f');
-
- -- 2^50 is too expensive to compute, but memoization makes it fast.
- SELECT f(50) as result;
- """,
- out=Csv("""
- "result"
- 1125899906842624
- """))
-
- def test_create_function_memoize_float(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- -- Compute 2^n inefficiently to test memoization.
- -- If it times out, memoization is not working.
- SELECT create_function('f(x INT)', 'FLOAT',
- '
- SELECT $x + 0.5
- ');
-
- SELECT EXPERIMENTAL_MEMOIZE('f');
-
- SELECT printf("%.1f", f(1)) as result
- UNION ALL
- SELECT printf("%.1f", f(1)) as result
- UNION ALL
- SELECT printf("%.1f", f(1)) as result
- """,
- out=Csv("""
- "result"
- "1.5"
- "1.5"
- "1.5"
- """))
-
- def test_create_function_memoize_intermittent_memoization(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- -- This function returns NULL for odd numbers and 1 for even numbers.
- -- As we do not memoize NULL results, we would only memoize the results
- -- for even numbers.
- SELECT create_function('f(x INT)', 'INT',
- '
- SELECT IIF($x = 0, 1,
- IIF(f($x - 1) IS NULL, 1, NULL)
- )
- ');
-
- SELECT EXPERIMENTAL_MEMOIZE('f');
-
- SELECT
- f(50) as f_50,
- f(51) as f_51;
- """,
- out=Csv("""
- "f_50","f_51"
- 1,"[NULL]"
- """))
-
- def test_create_function_memoize_subtree_size(self):
- # Tree:
- # 1
- # / \
- # / \
- # / \
- # 2 3
- # / \ / \
- # 4 5 6 7
- # / \ | | | \
- # 8 9 10 11 12 13
- # | |
- # 14 15
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- CREATE PERFETTO TABLE tree AS
- WITH data(id, parent_id) as (VALUES
- (1, NULL),
- (2, 1),
- (3, 1),
- (4, 2),
- (5, 2),
- (6, 3),
- (7, 3),
- (8, 4),
- (9, 4),
- (10, 5),
- (11, 6),
- (12, 7),
- (13, 7),
- (14, 8),
- (15, 9)
- )
- SELECT * FROM data;
-
- SELECT create_function('subtree_size(id INT)', 'INT',
- '
- SELECT 1 + IFNULL((
- SELECT
- SUM(subtree_size(child.id))
- FROM tree child
- WHERE child.parent_id = $id
- ), 0)
- ');
-
- SELECT EXPERIMENTAL_MEMOIZE('subtree_size');
-
- SELECT
- id, subtree_size(id) as size
- FROM tree
- ORDER BY id;
- """,
- out=Csv("""
- "id","size"
- 1,15
- 2,8
- 3,6
- 4,5
- 5,2
- 6,2
- 7,3
- 8,2
- 9,2
- 10,1
- 11,1
- 12,1
- 13,1
- 14,1
- 15,1
- """))
-
- def test_create_view_function(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT create_view_function('f(x INT)', 'result INT', 'SELECT $x + 1 as result');
-
- SELECT * FROM f(5);
- """,
- out=Csv("""
- "result"
- 6
- """))
-
- def test_first_non_null_frame(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- CREATE TABLE TEST(id INTEGER, val INTEGER);
-
- INSERT INTO TEST
- VALUES (1, 1), (2, NULL), (3, 3), (4, 4), (5, NULL), (6, NULL), (7, NULL);
-
- SELECT
- id,
- LAST_NON_NULL(val)
- OVER (ORDER BY id ASC ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS val
- FROM TEST
- ORDER BY id ASC;
- """,
- out=Csv("""
- "id","val"
- 1,3
- 2,4
- 3,4
- 4,4
- 5,"[NULL]"
- 6,"[NULL]"
- 7,"[NULL]"
- """))
-
- def test_first_non_null_partition(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- CREATE TABLE TEST(id INTEGER, part TEXT, val INTEGER);
-
- INSERT INTO TEST
- VALUES
- (1, 'A', 1),
- (2, 'A', NULL),
- (3, 'A', 3),
- (4, 'B', NULL),
- (5, 'B', 5),
- (6, 'B', NULL),
- (7, 'B', 7);
-
- SELECT id, LAST_NON_NULL(val) OVER (PARTITION BY part ORDER BY id ASC) AS val
- FROM TEST
- ORDER BY id ASC;
- """,
- out=Csv("""
- "id","val"
- 1,1
- 2,1
- 3,3
- 4,"[NULL]"
- 5,5
- 6,5
- 7,7
- """))
-
- def test_first_non_null(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- CREATE TABLE TEST(id INTEGER, val INTEGER);
-
- INSERT INTO TEST
- VALUES (1, 1), (2, NULL), (3, 3), (4, 4), (5, NULL), (6, NULL), (7, NULL);
-
- SELECT id, LAST_NON_NULL(val) OVER (ORDER BY id ASC) AS val
- FROM TEST
- ORDER BY id ASC;
- """,
- out=Csv("""
- "id","val"
- 1,1
- 2,1
- 3,3
- 4,4
- 5,4
- 6,4
- 7,4
- """))
-
- def test_spans_overlapping_dur_intersect_edge(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 2, 1, 2) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_intersect_edge_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(1, 2, 0, 2) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_intersect_all(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 3, 1, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_intersect_all_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(1, 1, 0, 3) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_no_intersect(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 1, 2, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
-
- def test_spans_overlapping_dur_no_intersect_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(2, 1, 0, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
-
- def test_spans_overlapping_dur_negative_dur(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, -1, 0, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
-
- def test_spans_overlapping_dur_negative_dur_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 1, 0, -1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
-
- def test_stacks(self):
- return DiffTestBlueprint(
- trace=DataPath("perf_sample.pb"),
- query="""
- SELECT HEX(
- CAT_STACKS(
- "A",
- CAT_STACKS(
- "B",
- CAT_STACKS(
- "C",
- STACK_FROM_STACK_PROFILE_CALLSITE(5),
- "D")
- ),
- "E",
- NULL,
- STACK_FROM_STACK_PROFILE_CALLSITE(14),
- STACK_FROM_STACK_PROFILE_CALLSITE(NULL),
- STACK_FROM_STACK_PROFILE_FRAME(4),
- STACK_FROM_STACK_PROFILE_FRAME(NULL)))
- """,
- out=BinaryProto(
- message_type="perfetto.protos.Stack",
- contents="""
- entries {
- frame_id: 4
- }
- entries {
- callsite_id: 14
- }
- entries {
- name: "E"
- }
- entries {
- name: "D"
- }
- entries {
- callsite_id: 5
- }
- entries {
- name: "C"
- }
- entries {
- name: "B"
- }
- entries {
- name: "A"
- }
- """))
-
- def test_profile_no_functions(self):
- return DiffTestBlueprint(
- trace=DataPath("perf_sample_no_functions.pb"),
- query="""
- SELECT HEX(
- EXPERIMENTAL_PROFILE(STACK_FROM_STACK_PROFILE_CALLSITE(callsite_id))
- )
- FROM PERF_SAMPLE
- """,
- out=BinaryProto(
- message_type="perfetto.third_party.perftools.profiles.Profile",
- post_processing=PrintProfileProto,
- contents="""
- Sample:
- Values: 1
- Stack:
- (0x7a4167d3f8)
- (0x783153c8e4)
- (0x7a4161ef8c)
- (0x7a42c3d8b0)
- (0x7a4167d9f4)
- (0x7a4163bc44)
- (0x7a4172f330)
- (0x7a4177a658)
- (0x7a4162b3a0)
-
- Sample:
- Values: 1
- Stack:
- (0x7a4167d9f8)
- (0x7a4163bc44)
- (0x7a4172f330)
- (0x7a4177a658)
- (0x7a4162b3a0)
- """))
-
- def test_profile_default_sample_types(self):
- return DiffTestBlueprint(
- trace=DataPath("perf_sample.pb"),
- query="""
- SELECT HEX(
- EXPERIMENTAL_PROFILE(
- CAT_STACKS(
- "A",
- STACK_FROM_STACK_PROFILE_CALLSITE(2),
- "B"
- )))
- """,
- out=BinaryProto(
- message_type="perfetto.third_party.perftools.profiles.Profile",
- post_processing=PrintProfileProto,
- contents="""
- Sample:
- Values: 1
- Stack:
- B (0x0)
- perfetto::base::UnixTaskRunner::Run() (0x7a4172f330)
- perfetto::ServiceMain(int, char**) (0x7a4177a658)
- __libc_init (0x7a4162b3a0)
- A (0x0)
- """))
-
- def test_profile_with_sample_types(self):
- return DiffTestBlueprint(
- trace=DataPath("perf_sample.pb"),
- query="""
- SELECT HEX(
- EXPERIMENTAL_PROFILE(
- CAT_STACKS("A", "B"), "type", "units", 42))
- """,
- out=BinaryProto(
- message_type="perfetto.third_party.perftools.profiles.Profile",
- post_processing=PrintProfileProto,
- contents="""
- Sample:
- Values: 42
- Stack:
- B (0x0)
- A (0x0)
- """))
-
- def test_profile_aggregates_samples(self):
- return DiffTestBlueprint(
- trace=DataPath("perf_sample.pb"),
- query="""
- WITH samples(stack, value) AS (
- VALUES
- (CAT_STACKS("A", "B"), 4),
- (CAT_STACKS("A", "B"), 8),
- (CAT_STACKS("A", "B"), 15),
- (CAT_STACKS("A", "C"), 16),
- (CAT_STACKS("C", "B"), 23),
- (CAT_STACKS("C", "B"), 42)
- )
- SELECT HEX(
- EXPERIMENTAL_PROFILE(
- stack, "type", "units", value))
- FROM samples
- """,
- out=BinaryProto(
- message_type="perfetto.third_party.perftools.profiles.Profile",
- post_processing=PrintProfileProto,
- contents="""
- Sample:
- Values: 16
- Stack:
- C (0x0)
- A (0x0)
-
- Sample:
- Values: 27
- Stack:
- B (0x0)
- A (0x0)
-
- Sample:
- Values: 65
- Stack:
- B (0x0)
- C (0x0)
- """))
-
- def test_annotated_callstack(self):
- return DiffTestBlueprint(
- trace=DataPath("perf_sample_annotations.pftrace"),
- query="""
- SELECT HEX(EXPERIMENTAL_PROFILE(STACK_FROM_STACK_PROFILE_CALLSITE(251, TRUE)))
- """,
- out=BinaryProto(
- message_type="perfetto.third_party.perftools.profiles.Profile",
- post_processing=PrintProfileProto,
- contents="""
- Sample:
- Values: 1
- Stack:
- art::ResolveFieldWithAccessChecks(art::Thread*, art::ClassLinker*, unsigned short, art::ArtMethod*, bool, bool, unsigned long) [common-frame] (0x724da79a74)
- NterpGetInstanceFieldOffset [common-frame-interp] (0x724da794b0)
- nterp_get_instance_field_offset [common-frame-interp] (0x724dcfc070)
- nterp_op_iget_object_slow_path [common-frame-interp] (0x724dcf5884)
- android.view.ViewRootImpl.notifyDrawStarted [interp] (0x7248f894d2)
- android.view.ViewRootImpl.performTraversals [aot] (0x71b8d378)
- android.view.ViewRootImpl.doTraversal [aot] (0x71b93220)
- android.view.ViewRootImpl$TraversalRunnable.run [aot] (0x71ab0384)
- android.view.Choreographer.doCallbacks [aot] (0x71a91b6c)
- android.view.Choreographer.doFrame [aot] (0x71a92550)
- android.view.Choreographer$FrameDisplayEventReceiver.run [aot] (0x71b26fb0)
- android.os.Handler.dispatchMessage [aot] (0x71975924)
- android.os.Looper.loopOnce [aot] (0x71978d6c)
- android.os.Looper.loop [aot] (0x719788a0)
- android.app.ActivityThread.main [aot] (0x717454cc)
- art_quick_invoke_static_stub [common-frame] (0x724db2de00)
- _jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long) [common-frame] (0x724db545ec)
- art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*) (.__uniq.165753521025965369065708152063621506277) (0x724db53ad0)
- art_jni_trampoline [common-frame] (0x6ff5c578)
- com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run [aot] (0x71c4ab6c)
- com.android.internal.os.ZygoteInit.main [aot] (0x71c54c7c)
- art_quick_invoke_static_stub (0x724db2de00)
- art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list) (0x724dc422a8)
- art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list) (0x724dcc57c8)
- _JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...) (0x74e1b03ca8)
- android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool) (0x74e1b0feac)
- main (0x63da9c354c)
- __libc_init (0x74ff4a0728)
- """))
-
- def test_layout(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
- """),
- query="""
- CREATE TABLE TEST(start INTEGER, end INTEGER);
-
- INSERT INTO TEST
- VALUES
- (1, 5),
- (2, 4),
- (3, 8),
- (6, 7),
- (6, 7),
- (6, 7);
-
- WITH custom_slices as (
- SELECT
- start as ts,
- end - start as dur
- FROM test
- )
- SELECT
- ts,
- INTERNAL_LAYOUT(ts, dur) over (
- order by ts
- rows between unbounded preceding and current row
- ) as depth
- FROM custom_slices
- """,
- out=Csv("""
- "ts","depth"
- 1,0
- 2,1
- 3,2
- 6,0
- 6,1
- 6,3
- """))
-
- def test_layout_with_instant_events(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
- """),
- query="""
- CREATE TABLE TEST(start INTEGER, end INTEGER);
-
- INSERT INTO TEST
- VALUES
- (1, 5),
- (2, 2),
- (3, 3),
- (4, 4);
-
- WITH custom_slices as (
- SELECT
- start as ts,
- end - start as dur
- FROM test
- )
- SELECT
- ts,
- INTERNAL_LAYOUT(ts, dur) over (
- order by ts
- rows between unbounded preceding and current row
- ) as depth
- FROM custom_slices
- """,
- out=Csv("""
- "ts","depth"
- 1,0
- 2,1
- 3,1
- 4,1
- """))
-
- def test_layout_with_events_without_end(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
- """),
- query="""
- CREATE TABLE TEST(ts INTEGER, dur INTEGER);
-
- INSERT INTO TEST
- VALUES
- (1, -1),
- (2, -1),
- (3, 5),
- (4, 1),
- (5, 1);
-
- SELECT
- ts,
- INTERNAL_LAYOUT(ts, dur) over (
- order by ts
- rows between unbounded preceding and current row
- ) as depth
- FROM test
- """,
- out=Csv("""
- "ts","depth"
- 1,0
- 2,1
- 3,2
- 4,3
- 5,3
- """))
-
- def test_math_ln_function(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT
- CAST(LN(1) * 1000 AS INTEGER) AS valid,
- LN("as") AS invalid_str,
- LN(NULL) AS invalid_null
- """,
- out=Csv("""
- "valid","invalid_str","invalid_null"
- 0,"[NULL]","[NULL]"
- """))
-
- def test_math_exp_function(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT
- CAST(EXP(1) * 1000 AS INTEGER) AS valid,
- EXP("asd") AS invalid_str,
- EXP(NULL) AS invalid_null
- """,
- out=Csv("""
- "valid","invalid_str","invalid_null"
- 2718,"[NULL]","[NULL]"
- """))
-
- def test_math_sqrt_function(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT
- CAST(SQRT(4) AS INTEGER) AS valid,
- SQRT("asd") AS invalid_str,
- SQRT(NULL) AS invalid_null
- """,
- out=Csv("""
- "valid","invalid_str","invalid_null"
- 2,"[NULL]","[NULL]"
- """))
-
- def test_math_functions(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- SELECT
- CAST(SQRT(EXP(LN(1))) AS INTEGER) AS valid,
- SQRT(EXP(LN("asd"))) AS invalid_str,
- SQRT(EXP(LN(NULL))) AS invalid_null
- """,
- out=Csv("""
- "valid","invalid_str","invalid_null"
- 1,"[NULL]","[NULL]"
- """))
-
- def test_table_function_drop_partial(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- CREATE TABLE bar AS SELECT 1;
-
- CREATE OR REPLACE PERFETTO FUNCTION foo()
- RETURNS TABLE(x INT) AS
- SELECT 1 AS x
- UNION
- SELECT * FROM bar;
-
- CREATE TABLE res AS SELECT * FROM foo() LIMIT 1;
-
- DROP TABLE bar;
- """,
- out=Csv(""))
diff --git a/test/trace_processor/diff_tests/syntax/perfetto_sql/tests.py b/test/trace_processor/diff_tests/syntax/include_tests.py
similarity index 74%
rename from test/trace_processor/diff_tests/syntax/perfetto_sql/tests.py
rename to test/trace_processor/diff_tests/syntax/include_tests.py
index d5e37b3..6c4d88a 100644
--- a/test/trace_processor/diff_tests/syntax/perfetto_sql/tests.py
+++ b/test/trace_processor/diff_tests/syntax/include_tests.py
@@ -19,23 +19,7 @@
from python.generators.diff_tests.testing import TestSuite
-class PerfettoSql(TestSuite):
-
- def test_create_perfetto_table_double_metric_run(self):
- return DiffTestBlueprint(
- trace=TextProto(r''),
- query="""
- SELECT RUN_METRIC('android/cpu_info.sql');
- SELECT RUN_METRIC('android/cpu_info.sql');
-
- SELECT * FROM cluster_core_type;
- """,
- out=Csv("""
- "cluster","core_type"
- 0,"little"
- 1,"big"
- 2,"bigger"
- """))
+class PerfettoInclude(TestSuite):
def test_import(self):
return DiffTestBlueprint(
@@ -157,31 +141,3 @@
"TRACE_START()"
1000
"""))
-
- def test_macro(self):
- return DiffTestBlueprint(
- trace=TextProto(''),
- query='''
- CREATE PERFETTO MACRO foo(a Expr,b Expr) RETURNS TableOrSubquery AS
- SELECT $a - $b;
- SELECT (foo!(123, 100)) as res;
- ''',
- out=Csv("""
- "res"
- 23
- """))
-
- def test_nested_macro(self):
- return DiffTestBlueprint(
- trace=TextProto(''),
- query='''
- CREATE PERFETTO MACRO foo(a Expr) returns Expr AS $a;
- CREATE PERFETTO MACRO bar(a Expr) returns Expr AS (SELECT $a);
- CREATE PERFETTO MACRO baz(a Expr,b Expr) returns TableOrSubquery AS
- SELECT bar!(foo!(123)) - $b as res;
- baz!(123, 100);
- ''',
- out=Csv("""
- "res"
- 23
- """))
diff --git a/test/trace_processor/diff_tests/syntax/macro_tests.py b/test/trace_processor/diff_tests/syntax/macro_tests.py
new file mode 100644
index 0000000..f34aaa0
--- /dev/null
+++ b/test/trace_processor/diff_tests/syntax/macro_tests.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import 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 PerfettoMacro(TestSuite):
+
+ def test_macro(self):
+ return DiffTestBlueprint(
+ trace=TextProto(''),
+ query='''
+ CREATE PERFETTO MACRO foo(a Expr,b Expr) RETURNS TableOrSubquery AS
+ SELECT $a - $b;
+ SELECT (foo!(123, 100)) as res;
+ ''',
+ out=Csv("""
+ "res"
+ 23
+ """))
+
+ def test_nested_macro(self):
+ return DiffTestBlueprint(
+ trace=TextProto(''),
+ query='''
+ CREATE PERFETTO MACRO foo(a Expr) returns Expr AS $a;
+ CREATE PERFETTO MACRO bar(a Expr) returns Expr AS (SELECT $a);
+ CREATE PERFETTO MACRO baz(a Expr,b Expr) returns TableOrSubquery AS
+ SELECT bar!(foo!(123)) - $b as res;
+ baz!(123, 100);
+ ''',
+ out=Csv("""
+ "res"
+ 23
+ """))
+
+ def test_replace_macro(self):
+ return DiffTestBlueprint(
+ trace=TextProto(''),
+ query='''
+ CREATE PERFETTO MACRO foo() RETURNS Expr AS 1;
+ CREATE OR REPLACE PERFETTO MACRO foo() RETURNS Expr AS 2;
+
+ SELECT foo!() as res;
+ ''',
+ out=Csv("""
+ "res"
+ 2
+ """))
diff --git a/test/trace_processor/diff_tests/syntax/table_function_tests.py b/test/trace_processor/diff_tests/syntax/table_function_tests.py
new file mode 100644
index 0000000..c2ccad7
--- /dev/null
+++ b/test/trace_processor/diff_tests/syntax/table_function_tests.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from python.generators.diff_tests.testing import PrintProfileProto
+from google.protobuf import text_format
+
+
+class PerfettoTableFunction(TestSuite):
+
+ def test_create_table_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ CREATE PERFETTO FUNCTION f(x INT) RETURNS TABLE(y INT) AS SELECT $x + 1 as y;
+
+ SELECT * FROM f(5);
+ """,
+ out=Csv("""
+ "y"
+ 6
+ """))
+
+ def test_replace_table_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ CREATE PERFETTO FUNCTION f(x INT) RETURNS TABLE(y INT) AS SELECT $x + 1 as y;
+ CREATE OR REPLACE PERFETTO FUNCTION f(x INT) RETURNS TABLE(y INT) AS SELECT $x + 2 as y;
+
+ SELECT * FROM f(5);
+ """,
+ out=Csv("""
+ "y"
+ 7
+ """))
+
+ def test_legacy_create_view_function(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT create_view_function('f(x INT)', 'result INT', 'SELECT $x + 1 as result');
+
+ SELECT * FROM f(5);
+ """,
+ out=Csv("""
+ "result"
+ 6
+ """))
+
+ def test_legacy_table_function_drop_partial(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ CREATE TABLE bar AS SELECT 1;
+
+ CREATE OR REPLACE PERFETTO FUNCTION foo()
+ RETURNS TABLE(x INT) AS
+ SELECT 1 AS x
+ UNION
+ SELECT * FROM bar;
+
+ CREATE TABLE res AS SELECT * FROM foo() LIMIT 1;
+
+ DROP TABLE bar;
+ """,
+ out=Csv(""))
diff --git a/test/trace_processor/diff_tests/syntax/table_tests.py b/test/trace_processor/diff_tests/syntax/table_tests.py
new file mode 100644
index 0000000..f675760
--- /dev/null
+++ b/test/trace_processor/diff_tests/syntax/table_tests.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import 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 PerfettoTable(TestSuite):
+
+ def test_create_table(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r''),
+ query="""
+ CREATE PERFETTO TABLE foo AS SELECT 42 as a;
+
+ SELECT * FROM foo;
+ """,
+ out=Csv("""
+ "a"
+ 42
+ """))
+
+ def test_replace_table(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r''),
+ query="""
+ CREATE PERFETTO TABLE foo AS SELECT 42 as a;
+ CREATE OR REPLACE PERFETTO TABLE foo AS SELECT 43 as a;
+
+ SELECT * FROM foo;
+ """,
+ out=Csv("""
+ "a"
+ 43
+ """))
+
+ def test_create_perfetto_table_double_metric_run(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r''),
+ query="""
+ SELECT RUN_METRIC('android/cpu_info.sql');
+ SELECT RUN_METRIC('android/cpu_info.sql');
+
+ SELECT * FROM cluster_core_type;
+ """,
+ out=Csv("""
+ "cluster","core_type"
+ 0,"little"
+ 1,"big"
+ 2,"bigger"
+ """))
diff --git a/test/trace_processor/diff_tests/syntax/view_tests.py b/test/trace_processor/diff_tests/syntax/view_tests.py
new file mode 100644
index 0000000..01b4d67
--- /dev/null
+++ b/test/trace_processor/diff_tests/syntax/view_tests.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import 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 PerfettoView(TestSuite):
+
+ def test_create_view(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r''),
+ query="""
+ CREATE PERFETTO VIEW foo AS SELECT 42 as a;
+
+ SELECT * FROM foo;
+ """,
+ out=Csv("""
+ "a"
+ 42
+ """))
+
+ def test_replace_view(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r''),
+ query="""
+ CREATE PERFETTO VIEW Foo AS SELECT 42 as a;
+ CREATE OR REPLACE PERFETTO VIEW Foo AS SELECT 43 as a;
+
+ SELECT * FROM foo;
+ """,
+ out=Csv("""
+ "a"
+ 43
+ """))
diff --git a/tools/check_sql_metrics.py b/tools/check_sql_metrics.py
index d1e937e..b577b43 100755
--- a/tools/check_sql_metrics.py
+++ b/tools/check_sql_metrics.py
@@ -34,24 +34,21 @@
from python.generators.sql_processing.utils import DROP_TABLE_VIEW_PATTERN
from python.generators.sql_processing.utils import CREATE_TABLE_VIEW_PATTERN
+# Allowlist path are relative to the metrics root.
CREATE_TABLE_ALLOWLIST = {
- ('/src/trace_processor/metrics/sql/android'
+ ('/android'
'/android_blocking_calls_cuj_metric.sql'): [
'android_cujs', 'relevant_binder_calls_with_names',
'android_blocking_calls_cuj_calls'
],
- '/src/trace_processor/metrics/sql/android/jank/cujs.sql': [
- 'android_jank_cuj'
- ],
- '/src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql': [
+ '/android/jank/cujs.sql': ['android_jank_cuj'],
+ '/chrome/gesture_flow_event.sql': [
'{{prefix}}_latency_info_flow_step_filtered'
],
- '/src/trace_processor/metrics/sql/chrome/gesture_jank.sql': [
+ '/chrome/gesture_jank.sql': [
'{{prefix}}_jank_maybe_null_prev_and_next_without_precompute'
],
- '/src/trace_processor/metrics/sql/experimental/frame_times.sql': [
- 'DisplayCompositorPresentationEvents'
- ]
+ '/experimental/frame_times.sql': ['DisplayCompositorPresentationEvents']
}
@@ -71,7 +68,7 @@
return res
-def check(path: str) -> List[str]:
+def check(path: str, metrics_sources: str) -> List[str]:
with open(path) as f:
sql = f.read()
@@ -82,6 +79,7 @@
sql, DROP_TABLE_VIEW_PATTERN)
errors = check_banned_create_table_as(sql,
path.split(ROOT_DIR)[1],
+ metrics_sources.split(ROOT_DIR)[1],
CREATE_TABLE_ALLOWLIST)
errors += check_banned_create_view_as(sql, path.split(ROOT_DIR)[1])
for name, [line, type] in create_table_view_dir.items():
@@ -110,7 +108,7 @@
for f in files:
path = os.path.join(root, f)
if path.endswith('.sql'):
- errors += check(path)
+ errors += check(path, metrics_sources)
if errors:
sys.stderr.write("\n".join(errors))
diff --git a/tools/check_sql_modules.py b/tools/check_sql_modules.py
index c697ac3..1cacd51 100755
--- a/tools/check_sql_modules.py
+++ b/tools/check_sql_modules.py
@@ -30,29 +30,29 @@
from python.generators.sql_processing.utils import check_banned_create_table_as
from python.generators.sql_processing.utils import check_banned_create_view_as
from python.generators.sql_processing.utils import check_banned_words
+from python.generators.sql_processing.utils import check_banned_include_all
+# Allowlist path are relative to the stdlib root.
CREATE_TABLE_ALLOWLIST = {
- '/src/trace_processor/perfetto_sql/stdlib/android/binder.sql': [
+ '/android/binder.sql': [
'internal_oom_score', 'internal_async_binder_reply',
'internal_binder_async_txn_raw'
],
- '/src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql': [
+ '/android/monitor_contention.sql': [
'internal_isolated', 'android_monitor_contention_chain',
'android_monitor_contention'
],
- '/src/trace_processor/perfetto_sql/stdlib/chrome/tasks.sql': [
+ '/chrome/tasks.sql': [
'internal_chrome_mojo_slices', 'internal_chrome_java_views',
'internal_chrome_scheduler_tasks', 'internal_chrome_tasks'
],
- ('/src/trace_processor/perfetto_sql/stdlib/experimental/'
+ ('/experimental/'
'thread_executing_span.sql'): [
'internal_wakeup', 'experimental_thread_executing_span_graph',
'internal_critical_path', 'internal_wakeup_graph',
'experimental_thread_executing_span_graph'
],
- '/src/trace_processor/perfetto_sql/stdlib/experimental/flat_slices.sql': [
- 'experimental_slice_flattened'
- ]
+ '/experimental/flat_slices.sql': ['experimental_slice_flattened']
}
@@ -110,13 +110,18 @@
if 'RUN_METRIC' in line:
errors.append(f"RUN_METRIC is banned in standard library.\n"
f"Offending file: {path}\n")
+ if 'insert into' in line.casefold():
+ errors.append(f"INSERT INTO table is not allowed in standard library.\n"
+ f"Offending file: {path}\n")
errors += parsed.errors
errors += check_banned_words(sql, path)
- errors += check_banned_create_table_as(sql,
- path.split(ROOT_DIR)[1],
- CREATE_TABLE_ALLOWLIST)
+ errors += check_banned_create_table_as(
+ sql,
+ path.split(ROOT_DIR)[1],
+ args.stdlib_sources.split(ROOT_DIR)[1], CREATE_TABLE_ALLOWLIST)
errors += check_banned_create_view_as(sql, path.split(ROOT_DIR)[1])
+ errors += check_banned_include_all(sql, path.split(ROOT_DIR)[1])
if errors:
sys.stderr.write("\n".join(errors))
diff --git a/ui/package.json b/ui/package.json
index 22ea23c..5d28841 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -1,7 +1,7 @@
{
- "name": "perfetto-ui",
+ "name": "perfetto-webui",
"version": "1.0.0",
- "description": "Perfetto UI",
+ "description": "Perfetto Web UI",
"repository": "https://android.googlesource.com/platform/external/perfetto",
"main": "main.js",
"author": "Perfetto Team",
diff --git a/ui/src/assets/widgets/menu.scss b/ui/src/assets/widgets/menu.scss
index 523ca9f..d61bc82 100644
--- a/ui/src/assets/widgets/menu.scss
+++ b/ui/src/assets/widgets/menu.scss
@@ -14,6 +14,13 @@
@import "theme";
+// If we're in a popup menu, remove the padding
+.pf-popup-menu.pf-popup {
+ .pf-popup-content {
+ padding: 0;
+ }
+}
+
.pf-menu {
display: flex;
flex-direction: column;
diff --git a/ui/src/assets/widgets/multiselect.scss b/ui/src/assets/widgets/multiselect.scss
index b0a19fe..85eba96 100644
--- a/ui/src/assets/widgets/multiselect.scss
+++ b/ui/src/assets/widgets/multiselect.scss
@@ -27,7 +27,6 @@
flex-direction: column;
align-items: stretch;
width: 280px;
- margin: 5px;
& > .pf-search-bar {
margin-bottom: 8px;
display: flex;
diff --git a/ui/src/assets/widgets/popup.scss b/ui/src/assets/widgets/popup.scss
index 5b3c0bd..0469940 100644
--- a/ui/src/assets/widgets/popup.scss
+++ b/ui/src/assets/widgets/popup.scss
@@ -28,6 +28,7 @@
.pf-popup-content {
// Ensures all content is rendered above the arrow
position: relative;
+ padding: 6px;
}
}
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index baecd18..5087d04 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -89,6 +89,17 @@
name: 'Total allocation count'
},
];
+ case ProfileType.MIXED_HEAP_PROFILE:
+ return [
+ {
+ option: FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
+ name: 'Total allocation size (malloc + java)'
+ },
+ {
+ option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_KEY,
+ name: 'Total allocation count (malloc + java)'
+ },
+ ];
default:
const exhaustiveCheck: never = profileType;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 3263787..64956a0 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -167,6 +167,7 @@
export enum ProfileType {
HEAP_PROFILE = 'heap_profile',
+ MIXED_HEAP_PROFILE = 'heap_profile:com.android.art,libc.malloc',
NATIVE_HEAP_PROFILE = 'heap_profile:libc.malloc',
JAVA_HEAP_SAMPLES = 'heap_profile:com.android.art',
JAVA_HEAP_GRAPH = 'graph',
diff --git a/ui/src/controller/aggregation/aggregation_controller.ts b/ui/src/controller/aggregation/aggregation_controller.ts
index a038d0a..0240228 100644
--- a/ui/src/controller/aggregation/aggregation_controller.ts
+++ b/ui/src/controller/aggregation/aggregation_controller.ts
@@ -154,7 +154,7 @@
// the purposes of aggregation, however the aggregation infrastructure
// is likely to be significantly reworked when we introduce EventSet,
// and the complexity of supporting bigints throughout the aggregation
- // panels in it's current form is not worth it. Thus, we simply
+ // panels in its current form is not worth it. Thus, we simply
// convert bigints to numbers.
column.data[i] = Number(item);
} else {
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index c5b33bc..3ff39f5 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -53,6 +53,7 @@
function getFlamegraphType(type: ProfileType) {
switch (type) {
case ProfileType.HEAP_PROFILE:
+ case ProfileType.MIXED_HEAP_PROFILE:
case ProfileType.NATIVE_HEAP_PROFILE:
case ProfileType.JAVA_HEAP_SAMPLES:
return 'native';
@@ -61,7 +62,8 @@
case ProfileType.PERF_SAMPLE:
return 'perf';
default:
- throw new Error(`Unexpected profile type ${profileType}`);
+ const exhaustiveCheck: never = type;
+ throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 5fb6b7b..4269ae5 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -676,7 +676,11 @@
if (profile.numRows() !== 1) return;
const row = profile.firstRow({ts: LONG, type: STR, upid: NUM});
const ts = Time.fromRaw(row.ts);
- const type = profileType(row.type);
+ let profType = row.type;
+ if (profType == 'heap_profile:libc.malloc,com.android.art') {
+ profType = 'heap_profile:com.android.art,libc.malloc';
+ }
+ const type = profileType(profType);
const upid = row.upid;
globals.dispatch(Actions.selectHeapProfile({id: 0, upid, ts, type}));
}
diff --git a/ui/src/frontend/flamegraph.ts b/ui/src/frontend/flamegraph.ts
index bf2554d..3407405 100644
--- a/ui/src/frontend/flamegraph.ts
+++ b/ui/src/frontend/flamegraph.ts
@@ -142,7 +142,7 @@
this.startingY = y;
- // For each node, we use this map to get information about it's parent
+ // For each node, we use this map to get information about its parent
// (total size of it, width and where it starts in graph) so we can
// calculate it's own position in graph.
const nodesMap = new Map<number, Node>();
diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts
index b1b7a57..bfed701 100644
--- a/ui/src/frontend/flamegraph_panel.ts
+++ b/ui/src/frontend/flamegraph_panel.ts
@@ -28,6 +28,8 @@
import {raf} from '../core/raf_scheduler';
import {Button} from '../widgets/button';
import {DurationWidget} from '../widgets/duration';
+import {Icon} from '../widgets/icon';
+import {Popup} from '../widgets/popup';
import {Flamegraph, NodeRendering} from './flamegraph';
import {globals} from './globals';
@@ -91,7 +93,17 @@
[
m('div.options',
[
- m('div.title', this.getTitle()),
+ m('div.title',
+ this.getTitle(),
+ (this.profileType === ProfileType.MIXED_HEAP_PROFILE) &&
+ m(Popup,
+ {
+ trigger: m(Icon, {icon: 'warning'}),
+ },
+ m('',
+ {style: {width: '300px'}},
+ 'This is a mixed java/native heap profile, free()s are not visualized. To visualize free()s, remove "all_heaps: true" from the config.')),
+ ':'),
this.getViewingOptionButtons(),
]),
m('div.details',
@@ -172,17 +184,20 @@
}
private getTitle(): string {
- switch (this.profileType!) {
+ const profileType = this.profileType!;
+ switch (profileType) {
+ case ProfileType.MIXED_HEAP_PROFILE:
+ return 'Mixed heap profile';
case ProfileType.HEAP_PROFILE:
- return 'Heap profile:';
+ return 'Heap profile';
case ProfileType.NATIVE_HEAP_PROFILE:
- return 'Native heap profile:';
+ return 'Native heap profile';
case ProfileType.JAVA_HEAP_SAMPLES:
- return 'Java heap samples:';
+ return 'Java heap samples';
case ProfileType.JAVA_HEAP_GRAPH:
- return 'Java heap graph:';
+ return 'Java heap graph';
case ProfileType.PERF_SAMPLE:
- return 'Profile:';
+ return 'Profile';
default:
throw new Error('unknown type');
}
@@ -192,9 +207,10 @@
if (this.profileType === undefined) {
return {};
}
+ const profileType = this.profileType;
const viewingOption: FlamegraphStateViewingOption =
globals.state.currentFlamegraphState!.viewingOption;
- switch (this.profileType) {
+ switch (profileType) {
case ProfileType.JAVA_HEAP_GRAPH:
if (viewingOption ===
FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY) {
@@ -202,13 +218,15 @@
} else {
return RENDER_SELF_AND_TOTAL;
}
+ case ProfileType.MIXED_HEAP_PROFILE:
case ProfileType.HEAP_PROFILE:
case ProfileType.NATIVE_HEAP_PROFILE:
case ProfileType.JAVA_HEAP_SAMPLES:
case ProfileType.PERF_SAMPLE:
return RENDER_SELF_AND_TOTAL;
default:
- throw new Error('unknown type');
+ const exhaustiveCheck: never = profileType;
+ throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
@@ -323,7 +341,7 @@
private static selectViewingOptions(profileType: ProfileType) {
const ret = [];
- for (let {option, name} of viewingOptions(profileType)) {
+ for (const {option, name} of viewingOptions(profileType)) {
ret.push(this.buildButtonComponent(option, name));
}
return ret;
diff --git a/ui/src/trace_processor/proto_ring_buffer.ts b/ui/src/trace_processor/proto_ring_buffer.ts
index 0aa5812..2c32cc8 100644
--- a/ui/src/trace_processor/proto_ring_buffer.ts
+++ b/ui/src/trace_processor/proto_ring_buffer.ts
@@ -19,7 +19,7 @@
// description. The architecture is identical.
const kGrowBytes = 128 * 1024;
-const kMaxMsgSize = 64 * 1024 * 1024;
+const kMaxMsgSize = 1024 * 1024 * 1024;
export class ProtoRingBuffer {
private buf = new Uint8Array(kGrowBytes);
diff --git a/ui/src/tracks/chrome_critical_user_interactions/details_panel.ts b/ui/src/tracks/chrome_critical_user_interactions/details_panel.ts
deleted file mode 100644
index f58e2e9..0000000
--- a/ui/src/tracks/chrome_critical_user_interactions/details_panel.ts
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import m from 'mithril';
-
-import {duration, Time, time} from '../../base/time';
-import {exists} from '../../base/utils';
-import {raf} from '../../core/raf_scheduler';
-import {
- BottomTab,
- bottomTabRegistry,
- NewBottomTabArgs,
-} from '../../frontend/bottom_tab';
-import {
- GenericSliceDetailsTabConfig,
-} from '../../frontend/generic_slice_details_tab';
-import {sqlValueToString} from '../../frontend/sql_utils';
-import {Timestamp} from '../../frontend/widgets/timestamp';
-import {LONG, LONG_NULL, NUM, STR} from '../../trace_processor/query_result';
-import {Anchor} from '../../widgets/anchor';
-import {DetailsShell} from '../../widgets/details_shell';
-import {DurationWidget} from '../../widgets/duration';
-import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
-import {Section} from '../../widgets/section';
-import {SqlRef} from '../../widgets/sql_ref';
-import {dictToTreeNodes, Tree} from '../../widgets/tree';
-
-interface PageLoadMetrics {
- url: string;
- navigationId: number;
- fcpDuration?: duration;
- lcpDuration?: duration;
- fcpTs: time, lcpTs?: time,
-}
-
-enum CriticalUserJourneyType {
- UNKNOWN = 'Unknown',
- PAGE_LOAD = 'PageLoad',
-}
-
-function convertToCriticalUserJourneyType(cujType: string):
- CriticalUserJourneyType {
- switch (cujType) {
- case CriticalUserJourneyType.PAGE_LOAD:
- return CriticalUserJourneyType.PAGE_LOAD;
- default:
- return CriticalUserJourneyType.UNKNOWN;
- }
-}
-
-interface Data {
- name: string;
- // Timestamp of the beginning of this slice in nanoseconds.
- ts: time;
- // Duration of this slice in nanoseconds.
- dur: duration;
- type: CriticalUserJourneyType;
- tableName: string;
- // Metrics for |type| = CriticalUserJourney.PAGE_LOAD
- pageLoadMetrics?: PageLoadMetrics;
-}
-
-export class CriticalUserInteractionDetailsPanel extends
- BottomTab<GenericSliceDetailsTabConfig> {
- static readonly kind = 'org.perfetto.CriticalUserInteractionDetailsPanel';
- data: Data|undefined;
- loaded = false;
-
- static create(args: NewBottomTabArgs): CriticalUserInteractionDetailsPanel {
- return new CriticalUserInteractionDetailsPanel(args);
- }
-
- constructor(args: NewBottomTabArgs) {
- super(args);
- this.loadData();
- }
-
- private async loadData() {
- const queryResult = await this.engine.query(`
- SELECT
- name,
- ts,
- dur,
- type AS tableName
- FROM chrome_interactions
- WHERE scoped_id = ${this.config.id}`);
-
- const iter = queryResult.firstRow({
- name: STR,
- ts: LONG,
- dur: LONG,
- tableName: STR,
- });
-
- this.data = {
- name: iter.name,
- ts: Time.fromRaw(iter.ts),
- dur: iter.dur,
- type: convertToCriticalUserJourneyType(iter.name),
- tableName: iter.tableName,
- };
-
- await this.loadMetrics();
-
- this.loaded = true;
- raf.scheduleFullRedraw();
- }
-
- private async loadMetrics() {
- if (exists(this.data)) {
- switch (this.data.type) {
- case CriticalUserJourneyType.PAGE_LOAD:
- await this.loadPageLoadMetrics();
- break;
- default:
- break;
- }
- }
- }
-
- private async loadPageLoadMetrics() {
- if (exists(this.data)) {
- const queryResult = await this.engine.query(`
- SELECT
- navigation_id AS navigationId,
- url,
- fcp AS fcpDuration,
- lcp AS lcpDuration,
- fcp_ts AS fcpTs,
- lcp_ts AS lcpTs
- FROM chrome_page_loads
- WHERE navigation_id = ${this.config.id}`);
-
- const iter = queryResult.firstRow({
- navigationId: NUM,
- url: STR,
- fcpDuration: LONG_NULL,
- lcpDuration: LONG_NULL,
- fcpTs: LONG,
- lcpTs: LONG,
- });
-
- this.data.pageLoadMetrics = {
- navigationId: iter.navigationId,
- url: iter.url,
- fcpTs: Time.fromRaw(iter.fcpTs),
- };
-
- if (exists(iter.fcpDuration)) {
- this.data.pageLoadMetrics.fcpDuration = iter.fcpDuration;
- }
-
- if (exists(iter.lcpDuration)) {
- this.data.pageLoadMetrics.lcpDuration = iter.lcpDuration;
- }
-
- if (Number(iter.lcpTs) != 0) {
- this.data.pageLoadMetrics.lcpTs = Time.fromRaw(iter.lcpTs);
- }
- }
- }
-
- private renderDetailsDictionary(): m.Child[] {
- const details: {[key: string]: m.Child} = {};
- if (exists(this.data)) {
- details['Name'] = sqlValueToString(this.data.name);
- details['Timestamp'] = m(Timestamp, {ts: this.data.ts});
- if (exists(this.data.pageLoadMetrics)) {
- details['FCP Timestamp'] =
- m(Timestamp, {ts: this.data.pageLoadMetrics.fcpTs});
- if (exists(this.data.pageLoadMetrics.fcpDuration)) {
- details['FCP Duration'] =
- m(DurationWidget, {dur: this.data.pageLoadMetrics.fcpDuration});
- }
- if (exists(this.data.pageLoadMetrics.lcpTs)) {
- details['LCP Timestamp'] =
- m(Timestamp, {ts: this.data.pageLoadMetrics.lcpTs});
- }
- if (exists(this.data.pageLoadMetrics.lcpDuration)) {
- details['LCP Duration'] =
- m(DurationWidget, {dur: this.data.pageLoadMetrics.lcpDuration});
- }
- details['Navigation ID'] = this.data.pageLoadMetrics.navigationId;
- const url = this.data.pageLoadMetrics.url;
- details['URL'] =
- m(Anchor, {href: url, target: '_blank', icon: 'open_in_new'}, url);
- }
- details['SQL ID'] =
- m(SqlRef, {table: 'chrome_interactions', id: this.config.id});
- }
-
- return dictToTreeNodes(details);
- }
-
- viewTab() {
- if (this.data === undefined) {
- return m('h2', 'Loading');
- }
-
- return m(
- DetailsShell,
- {
- title: this.getTitle(),
- },
- m(GridLayout,
- m(
- GridLayoutColumn,
- m(
- Section,
- {title: 'Details'},
- m(Tree, this.renderDetailsDictionary()),
- ),
- )));
- }
-
- getTitle(): string {
- return this.config.title;
- }
-
- isLoading() {
- return !this.loaded;
- }
-}
-
-bottomTabRegistry.register(CriticalUserInteractionDetailsPanel);
diff --git a/ui/src/tracks/chrome_critical_user_interactions/index.ts b/ui/src/tracks/chrome_critical_user_interactions/index.ts
index b77fc31..f77b967 100644
--- a/ui/src/tracks/chrome_critical_user_interactions/index.ts
+++ b/ui/src/tracks/chrome_critical_user_interactions/index.ts
@@ -16,8 +16,14 @@
import {Actions} from '../../common/actions';
import {SCROLLING_TRACK_GROUP} from '../../common/state';
+import {OnSliceClickArgs} from '../../frontend/base_slice_track';
+import {GenericSliceDetailsTab} from '../../frontend/generic_slice_details_tab';
import {globals} from '../../frontend/globals';
-import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
+import {
+ NAMED_SLICE_ROW,
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
+import {Slice} from '../../frontend/slice';
import {NewTrackArgs, TrackBase} from '../../frontend/track';
import {
Plugin,
@@ -25,6 +31,7 @@
PluginContextTrace,
PluginDescriptor,
PrimaryTrackSortKey,
+ STR,
} from '../../public';
import {
CustomSqlDetailsPanelConfig,
@@ -33,13 +40,45 @@
CustomSqlTableSliceTrack,
} from '../custom_sql_table_slices';
-import {CriticalUserInteractionDetailsPanel} from './details_panel';
+import {PageLoadDetailsPanel} from './page_load_details_panel';
export const CRITICAL_USER_INTERACTIONS_KIND =
- 'org.chromium.TopLevelScrolls.scrolls';
+ 'org.chromium.CriticalUserInteraction.track';
+
+export const CRITICAL_USER_INTERACTIONS_SLICE_ROW = {
+ ...NAMED_SLICE_ROW,
+ type: STR,
+};
+export type CriticalUserInteractionSliceRow =
+ typeof CRITICAL_USER_INTERACTIONS_SLICE_ROW;
+
+export interface CriticalUserInteractionSlice extends Slice {
+ type: string;
+}
+
+export interface CriticalUserInteractionSliceTrackTypes extends
+ NamedSliceTrackTypes {
+ slice: CriticalUserInteractionSlice;
+ row: CriticalUserInteractionSliceRow;
+}
+
+enum CriticalUserInteractionType {
+ UNKNOWN = 'Unknown',
+ PAGE_LOAD = 'chrome_page_loads',
+}
+
+function convertToCriticalUserInteractionType(cujType: string):
+ CriticalUserInteractionType {
+ switch (cujType) {
+ case CriticalUserInteractionType.PAGE_LOAD:
+ return CriticalUserInteractionType.PAGE_LOAD;
+ default:
+ return CriticalUserInteractionType.UNKNOWN;
+ }
+}
export class CriticalUserInteractionTrack extends
- CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
+ CustomSqlTableSliceTrack<CriticalUserInteractionSliceTrackTypes> {
static readonly kind = CRITICAL_USER_INTERACTIONS_KIND;
static create(args: NewTrackArgs): TrackBase {
@@ -53,14 +92,31 @@
};
}
- getDetailsPanel(): CustomSqlDetailsPanelConfig {
- return {
- kind: CriticalUserInteractionDetailsPanel.kind,
+ getDetailsPanel(
+ args: OnSliceClickArgs<CriticalUserInteractionSliceTrackTypes['slice']>):
+ CustomSqlDetailsPanelConfig {
+ let detailsPanel = {
+ kind: GenericSliceDetailsTab.kind,
config: {
sqlTableName: this.tableName,
- title: 'Chrome Critical User Interaction',
+ title: 'Chrome Interaction',
},
};
+
+ switch (convertToCriticalUserInteractionType(args.slice.type)) {
+ case CriticalUserInteractionType.PAGE_LOAD:
+ detailsPanel = {
+ kind: PageLoadDetailsPanel.kind,
+ config: {
+ sqlTableName: this.tableName,
+ title: 'Chrome Page Load',
+ },
+ };
+ break;
+ default:
+ break;
+ }
+ return detailsPanel;
}
getSqlImports(): CustomSqlImportConfig {
@@ -68,6 +124,17 @@
modules: ['chrome.interactions'],
};
}
+
+ getRowSpec(): CriticalUserInteractionSliceTrackTypes['row'] {
+ return CRITICAL_USER_INTERACTIONS_SLICE_ROW;
+ }
+
+ rowToSlice(row: CriticalUserInteractionSliceTrackTypes['row']):
+ CriticalUserInteractionSliceTrackTypes['slice'] {
+ const baseSlice = super.rowToSlice(row);
+ const type = row.type;
+ return {...baseSlice, type};
+ }
}
export function addCriticalUserInteractionTrack() {
diff --git a/ui/src/tracks/chrome_critical_user_interactions/page_load_details_panel.ts b/ui/src/tracks/chrome_critical_user_interactions/page_load_details_panel.ts
new file mode 100644
index 0000000..6808f39
--- /dev/null
+++ b/ui/src/tracks/chrome_critical_user_interactions/page_load_details_panel.ts
@@ -0,0 +1,200 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+
+import {duration, time, Time} from '../../base/time';
+import {exists} from '../../base/utils';
+import {raf} from '../../core/raf_scheduler';
+import {
+ BottomTab,
+ bottomTabRegistry,
+ NewBottomTabArgs,
+} from '../../frontend/bottom_tab';
+import {
+ GenericSliceDetailsTabConfig,
+} from '../../frontend/generic_slice_details_tab';
+import {Timestamp} from '../../frontend/widgets/timestamp';
+import {LONG, LONG_NULL, NUM, STR} from '../../trace_processor/query_result';
+import {Anchor} from '../../widgets/anchor';
+import {DetailsShell} from '../../widgets/details_shell';
+import {DurationWidget} from '../../widgets/duration';
+import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
+import {Section} from '../../widgets/section';
+import {SqlRef} from '../../widgets/sql_ref';
+import {dictToTreeNodes, Tree} from '../../widgets/tree';
+
+interface Data {
+ ts: time;
+ url: string;
+ navigationId: number;
+ fcpDuration: duration;
+ lcpDuration?: duration;
+ fcpTs: time;
+ lcpTs?: time;
+ domContentLoadedTs?: time;
+ loadTs?: time;
+ markFullyLoadedTs?: time;
+ markFullyVisibleTs?: time;
+ markInteractiveTs?: time;
+}
+
+export class PageLoadDetailsPanel extends
+ BottomTab<GenericSliceDetailsTabConfig> {
+ static readonly kind = 'org.perfetto.PageLoadDetailsPanel';
+ private loaded = false;
+ private data: Data|undefined;
+
+ static create(args: NewBottomTabArgs): PageLoadDetailsPanel {
+ return new PageLoadDetailsPanel(args);
+ }
+
+ constructor(args: NewBottomTabArgs) {
+ super(args);
+ this.loadData();
+ }
+
+ private async loadData() {
+ const queryResult = await this.engine.query(`
+ SELECT
+ navigation_id AS navigationId,
+ navigation_start_ts AS ts,
+ url,
+ fcp AS fcpDuration,
+ lcp AS lcpDuration,
+ fcp_ts AS fcpTs,
+ lcp_ts AS lcpTs,
+ dom_content_loaded_event_ts AS domContentLoadedTs,
+ load_event_ts AS loadTs,
+ mark_fully_loaded_ts AS markFullyLoadedTs,
+ mark_fully_visible_ts AS markFullyVisibleTs,
+ mark_interactive_ts AS markInteractiveTs
+ FROM chrome_page_loads
+ WHERE navigation_id = ${this.config.id};`);
+
+ const iter = queryResult.firstRow({
+ navigationId: NUM,
+ ts: LONG,
+ url: STR,
+ fcpDuration: LONG,
+ lcpDuration: LONG_NULL,
+ fcpTs: LONG,
+ lcpTs: LONG_NULL,
+ domContentLoadedTs: LONG_NULL,
+ loadTs: LONG_NULL,
+ markFullyLoadedTs: LONG_NULL,
+ markFullyVisibleTs: LONG_NULL,
+ markInteractiveTs: LONG_NULL,
+ });
+
+ this.data = {
+ ts: Time.fromRaw(iter.ts),
+ fcpTs: Time.fromRaw(iter.fcpTs),
+ fcpDuration: iter.fcpDuration,
+ navigationId: iter.navigationId,
+ url: iter.url,
+ lcpTs: Time.fromRaw(iter.lcpTs ?? undefined),
+ lcpDuration: iter.lcpDuration ?? undefined,
+ domContentLoadedTs: Time.fromRaw(iter.domContentLoadedTs ?? undefined),
+ loadTs: Time.fromRaw(iter.loadTs ?? undefined),
+ markFullyLoadedTs: Time.fromRaw(iter.markFullyLoadedTs ?? undefined),
+ markFullyVisibleTs: Time.fromRaw(iter.markFullyVisibleTs ?? undefined),
+ markInteractiveTs: Time.fromRaw(iter.markInteractiveTs ?? undefined),
+ };
+
+ this.loaded = true;
+ raf.scheduleFullRedraw();
+ }
+
+ private getDetailsDictionary() {
+ const details: {[key: string]: m.Child} = {};
+ if (exists(this.data)) {
+ details['Timestamp'] = m(Timestamp, {ts: this.data.ts});
+ details['FCP Timestamp'] = m(Timestamp, {ts: this.data.fcpTs});
+ details['FCP Duration'] = m(DurationWidget, {dur: this.data.fcpDuration});
+
+ if (exists(this.data.lcpTs)) {
+ details['LCP Timestamp'] = m(Timestamp, {ts: this.data.lcpTs});
+ }
+ if (exists(this.data.lcpDuration)) {
+ details['LCP Duration'] =
+ m(DurationWidget, {dur: this.data.lcpDuration});
+ }
+
+ if (exists(this.data.domContentLoadedTs)) {
+ details['DOM Content Loaded Event Timestamp'] =
+ m(Timestamp, {ts: this.data.domContentLoadedTs});
+ }
+
+ if (exists(this.data.loadTs)) {
+ details['Load Timestamp'] = m(Timestamp, {ts: this.data.loadTs});
+ }
+
+ if (exists(this.data.markFullyLoadedTs)) {
+ details['Page Timing Mark Fully Loaded Timestamp'] =
+ m(Timestamp, {ts: this.data.markFullyLoadedTs});
+ }
+
+ if (exists(this.data.markFullyVisibleTs)) {
+ details['Page Timing Mark Fully Visible Timestamp'] =
+ m(Timestamp, {ts: this.data.markFullyVisibleTs});
+ }
+
+ if (exists(this.data.markInteractiveTs)) {
+ details['Page Timing Mark Interactive Timestamp'] =
+ m(Timestamp, {ts: this.data.markInteractiveTs});
+ }
+
+ details['Navigation ID'] = this.data.navigationId;
+ details['URL'] =
+ m(Anchor,
+ {href: this.data.url, target: '_blank', icon: 'open_in_new'},
+ this.data.url);
+ details['SQL ID'] =
+ m(SqlRef, {table: 'chrome_page_loads', id: this.data.navigationId});
+ }
+ return details;
+ }
+
+ viewTab() {
+ if (this.isLoading()) {
+ return m('h2', 'Loading');
+ }
+
+ return m(
+ DetailsShell,
+ {
+ title: this.getTitle(),
+ },
+ m(GridLayout,
+ m(
+ GridLayoutColumn,
+ m(
+ Section,
+ {title: 'Details'},
+ m(Tree, dictToTreeNodes(this.getDetailsDictionary())),
+ ),
+ )));
+ }
+
+ getTitle(): string {
+ return this.config.title;
+ }
+
+ isLoading() {
+ return !this.loaded;
+ }
+}
+
+bottomTabRegistry.register(PageLoadDetailsPanel);
diff --git a/ui/src/tracks/custom_sql_table_slices/index.ts b/ui/src/tracks/custom_sql_table_slices/index.ts
index 8389a8e..40c980a 100644
--- a/ui/src/tracks/custom_sql_table_slices/index.ts
+++ b/ui/src/tracks/custom_sql_table_slices/index.ts
@@ -64,7 +64,9 @@
abstract getSqlDataSource(): CustomSqlTableDefConfig;
// Override by subclasses.
- abstract getDetailsPanel(): CustomSqlDetailsPanelConfig;
+ abstract getDetailsPanel(args:
+ OnSliceClickArgs<NamedSliceTrackTypes['slice']>):
+ CustomSqlDetailsPanelConfig;
getSqlImports(): CustomSqlImportConfig {
return {
@@ -106,11 +108,11 @@
}
onSliceClick(args: OnSliceClickArgs<NamedSliceTrackTypes['slice']>) {
- if (this.getDetailsPanel() === undefined) {
+ if (this.getDetailsPanel(args) === undefined) {
return;
}
- const detailsPanelConfig = this.getDetailsPanel();
+ const detailsPanelConfig = this.getDetailsPanel(args);
globals.makeSelection(Actions.selectGenericSlice({
id: args.slice.id,
sqlTableName: this.tableName,
diff --git a/ui/src/tracks/heap_profile/index.ts b/ui/src/tracks/heap_profile/index.ts
index 7b73dec..25617c5 100644
--- a/ui/src/tracks/heap_profile/index.ts
+++ b/ui/src/tracks/heap_profile/index.ts
@@ -85,7 +85,12 @@
const it = queryRes.iter({ts: LONG, type: STR});
for (let row = 0; it.valid(); it.next(), row++) {
data.tsStarts[row] = it.ts;
- data.types[row] = profileType(it.type);
+
+ let profType = it.type;
+ if (profType == 'heap_profile:libc.malloc,com.android.art') {
+ profType = 'heap_profile:com.android.art,libc.malloc';
+ }
+ data.types[row] = profileType(profType);
}
return data;
}
diff --git a/ui/src/widgets/menu.ts b/ui/src/widgets/menu.ts
index db358f8..275f07c 100644
--- a/ui/src/widgets/menu.ts
+++ b/ui/src/widgets/menu.ts
@@ -163,6 +163,7 @@
{
trigger,
position: popupPosition,
+ className: 'pf-popup-menu',
...popupAttrs,
},
m(Menu, children));