Merge "trace_processor: make diff test more Win-friendly"
diff --git a/Android.bp b/Android.bp
index 3376ba3..046da6e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6402,6 +6402,7 @@
filegroup {
name: "perfetto_src_base_base",
srcs: [
+ "src/base/ctrl_c_handler.cc",
"src/base/event_fd.cc",
"src/base/file_utils.cc",
"src/base/logging.cc",
diff --git a/BUILD b/BUILD
index 8d3c5ea..d91e53f 100644
--- a/BUILD
+++ b/BUILD
@@ -291,6 +291,7 @@
srcs = [
"include/perfetto/ext/base/circular_queue.h",
"include/perfetto/ext/base/container_annotations.h",
+ "include/perfetto/ext/base/ctrl_c_handler.h",
"include/perfetto/ext/base/endian.h",
"include/perfetto/ext/base/event_fd.h",
"include/perfetto/ext/base/file_utils.h",
@@ -574,6 +575,7 @@
perfetto_cc_library(
name = "src_base_base",
srcs = [
+ "src/base/ctrl_c_handler.cc",
"src/base/event_fd.cc",
"src/base/file_utils.cc",
"src/base/logging.cc",
diff --git a/examples/sdk/CMakeLists.txt b/examples/sdk/CMakeLists.txt
index 1e1c6e2..a2f74bd 100644
--- a/examples/sdk/CMakeLists.txt
+++ b/examples/sdk/CMakeLists.txt
@@ -47,4 +47,25 @@
target_link_libraries(example_system_wide log)
target_link_libraries(example_custom_data_source log)
target_link_libraries(example_console log)
-endif (ANDROID)
\ No newline at end of file
+endif (ANDROID)
+
+if (WIN32)
+ # The perfetto library contains many symbols, so it needs the big object
+ # format.
+ target_compile_options(perfetto PRIVATE "/bigobj")
+ # Disable legacy features in windows.h.
+ add_definitions(-DWIN32_LEAN_AND_MEAN -DNOMINMAX)
+ # On Windows we should link to WinSock2.
+ target_link_libraries(example ws2_32)
+ target_link_libraries(example_system_wide ws2_32)
+ target_link_libraries(example_custom_data_source ws2_32)
+ target_link_libraries(example_console ws2_32)
+endif (WIN32)
+
+# Enable standards-compliant mode when using the Visual Studio compiler.
+if (MSVC)
+ target_compile_options(example PRIVATE "/permissive-")
+ target_compile_options(example_system_wide PRIVATE "/permissive-")
+ target_compile_options(example_custom_data_source PRIVATE "/permissive-")
+ target_compile_options(example_console PRIVATE "/permissive-")
+endif (MSVC)
\ No newline at end of file
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index 694d23d..15e1799 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -18,6 +18,7 @@
sources = [
"circular_queue.h",
"container_annotations.h",
+ "ctrl_c_handler.h",
"endian.h",
"event_fd.h",
"file_utils.h",
diff --git a/include/perfetto/ext/base/ctrl_c_handler.h b/include/perfetto/ext/base/ctrl_c_handler.h
new file mode 100644
index 0000000..6c7b34c
--- /dev/null
+++ b/include/perfetto/ext/base/ctrl_c_handler.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
+
+namespace perfetto {
+namespace base {
+
+// On Linux/Android/Mac: installs SIGINT + SIGTERM signal handlers.
+// On Windows: installs a SetConsoleCtrlHandler() handler.
+// The passed handler must be async safe.
+using CtrlCHandlerFunction = void (*)();
+void InstallCtrCHandler(CtrlCHandlerFunction);
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
diff --git a/include/perfetto/profiling/memory/heap_profile.h b/include/perfetto/profiling/memory/heap_profile.h
index 1a780d1..d788e46 100644
--- a/include/perfetto/profiling/memory/heap_profile.h
+++ b/include/perfetto/profiling/memory/heap_profile.h
@@ -137,12 +137,6 @@
// Takes ownership of |info|.
uint32_t AHeapProfile_registerHeap(AHeapInfo* _Nullable info);
-// Called by libc upon receipt of the profiling signal.
-// DO NOT CALL EXCEPT FROM LIBC!
-// TODO(fmayer): Maybe move this out of this header.
-bool AHeapProfile_initSession(void* _Nullable (*_Nonnull malloc_fn)(size_t),
- void (*_Nonnull free_fn)(void* _Nullable));
-
// Reports an allocation of |size| on the given |heap_id|.
//
// If a profiling session is active, this function decides whether the reported
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 52003ce..76921cf 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -19,6 +19,10 @@
import("../../gn/test.gni")
import("../../gn/wasm.gni")
+# On standalone builds this is all the OSes we support. On chromium builds,
+# though, this really means !is_fuchsia && !is_nacl.
+_subprocess_supported = is_linux || is_android || is_mac || is_win
+
perfetto_component("base") {
deps = [ "../../gn:default_deps" ]
public_deps = [
@@ -26,6 +30,7 @@
"../../include/perfetto/ext/base",
]
sources = [
+ "ctrl_c_handler.cc",
"event_fd.cc",
"file_utils.cc",
"logging.cc",
@@ -53,11 +58,14 @@
}
if (!is_nacl) {
+ sources += [ "unix_task_runner.cc" ]
+ }
+
+ if (_subprocess_supported) {
sources += [
"subprocess.cc",
"subprocess_posix.cc",
"subprocess_windows.cc",
- "unix_task_runner.cc",
]
}
@@ -160,7 +168,7 @@
"uuid_unittest.cc",
"weak_ptr_unittest.cc",
]
- if (is_linux || is_android || is_mac || is_win) {
+ if (_subprocess_supported) {
# Don't run on Fuchsia, NaCL. They pretend to be POSIX but then give up on
# execve(2).
sources += [ "subprocess_unittest.cc" ]
diff --git a/src/base/ctrl_c_handler.cc b/src/base/ctrl_c_handler.cc
new file mode 100644
index 0000000..72b60a8
--- /dev/null
+++ b/src/base/ctrl_c_handler.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/ctrl_c_handler.h"
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <consoleapi.h>
+#include <io.h>
+#else
+#include <signal.h>
+#include <unistd.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+namespace {
+CtrlCHandlerFunction g_handler = nullptr;
+}
+
+void InstallCtrCHandler(CtrlCHandlerFunction handler) {
+ PERFETTO_CHECK(g_handler == nullptr);
+ g_handler = handler;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ auto trampoline = [](DWORD type) -> int {
+ if (type == CTRL_C_EVENT) {
+ g_handler();
+ return true;
+ }
+ return false;
+ };
+ ::SetConsoleCtrlHandler(trampoline, true);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ // Setup signal handler.
+ struct sigaction sa {};
+
+// Glibc headers for sa_sigaction trigger this.
+#pragma GCC diagnostic push
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
+#endif
+ sa.sa_handler = [](int) { g_handler(); };
+ sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
+#pragma GCC diagnostic pop
+ sigaction(SIGINT, &sa, nullptr);
+ sigaction(SIGTERM, &sa, nullptr);
+#else
+ // Do nothing on NaCL and Fuchsia.
+ ignore_result(handler);
+#endif
+}
+
+} // namespace base
+} // namespace perfetto
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index b8e332c..2610154 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -20,7 +20,6 @@
#include <fcntl.h>
#include <getopt.h>
-#include <signal.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -45,6 +44,7 @@
#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/time.h"
+#include "perfetto/ext/base/ctrl_c_handler.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/ext/base/thread_utils.h"
@@ -920,20 +920,7 @@
}
void PerfettoCmd::SetupCtrlCSignalHandler() {
- // Setup signal handler.
- struct sigaction sa {};
-
-// Glibc headers for sa_sigaction trigger this.
-#pragma GCC diagnostic push
-#if defined(__clang__)
-#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
-#endif
- sa.sa_handler = [](int) { g_consumer_cmd->SignalCtrlC(); };
- sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
-#pragma GCC diagnostic pop
- sigaction(SIGINT, &sa, nullptr);
- sigaction(SIGTERM, &sa, nullptr);
-
+ base::InstallCtrCHandler([] { g_consumer_cmd->SignalCtrlC(); });
task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [this] {
PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
ctrl_c_evt_.Clear();
diff --git a/src/profiling/memory/client_api.cc b/src/profiling/memory/client_api.cc
index f508522..5c911d2 100644
--- a/src/profiling/memory/client_api.cc
+++ b/src/profiling/memory/client_api.cc
@@ -15,6 +15,7 @@
*/
#include "perfetto/profiling/memory/heap_profile.h"
+#include "src/profiling/memory/heap_profile_internal.h"
#include <inttypes.h>
#include <malloc.h>
diff --git a/src/profiling/memory/client_api_factory_standalone.cc b/src/profiling/memory/client_api_factory_standalone.cc
index fa7c0c9..6b96f7a 100644
--- a/src/profiling/memory/client_api_factory_standalone.cc
+++ b/src/profiling/memory/client_api_factory_standalone.cc
@@ -25,6 +25,7 @@
#include "perfetto/profiling/memory/heap_profile.h"
#include "src/profiling/common/proc_utils.h"
#include "src/profiling/memory/client.h"
+#include "src/profiling/memory/heap_profile_internal.h"
#include "src/profiling/memory/heapprofd_producer.h"
#include <string>
diff --git a/src/profiling/memory/client_api_unittest.cc b/src/profiling/memory/client_api_unittest.cc
index 7301d03..17edb31 100644
--- a/src/profiling/memory/client_api_unittest.cc
+++ b/src/profiling/memory/client_api_unittest.cc
@@ -17,6 +17,7 @@
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/unix_task_runner.h"
#include "perfetto/profiling/memory/heap_profile.h"
+#include "src/profiling/memory/heap_profile_internal.h"
#include "src/profiling/memory/client.h"
#include "src/profiling/memory/client_api_factory.h"
diff --git a/src/profiling/memory/heap_profile_internal.h b/src/profiling/memory/heap_profile_internal.h
new file mode 100644
index 0000000..8e9a980
--- /dev/null
+++ b/src/profiling/memory/heap_profile_internal.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_MEMORY_HEAP_PROFILE_INTERNAL_H_
+#define SRC_PROFILING_MEMORY_HEAP_PROFILE_INTERNAL_H_
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#pragma GCC diagnostic push
+
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Wnullability-extension"
+#else
+#define _Nullable
+#define _Nonnull
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Called by the standalone client or libc upon receipt of the profiling
+// signal.
+bool AHeapProfile_initSession(void* _Nullable (*_Nonnull malloc_fn)(size_t),
+ void (*_Nonnull free_fn)(void* _Nullable));
+
+#ifdef __cplusplus
+}
+#endif
+
+#pragma GCC diagnostic pop
+
+#endif // SRC_PROFILING_MEMORY_HEAP_PROFILE_INTERNAL_H_
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index 9233b42..5f27236 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -318,6 +318,42 @@
}
}
+void __attribute__((constructor(1024))) RunAccurateSample() {
+ const char* a0 = getenv("HEAPPROFD_TESTING_RUN_ACCURATE_SAMPLE");
+ if (a0 == nullptr)
+ return;
+
+ static std::atomic<bool> initialized{false};
+ static uint32_t heap_id =
+ AHeapProfile_registerHeap(AHeapInfo_setEnabledCallback(
+ AHeapInfo_create("test"),
+ [](void*, const AHeapProfileEnableCallbackInfo*) {
+ initialized = true;
+ },
+ nullptr));
+
+ ChildFinishHandshake();
+
+ // heapprofd_client needs malloc to see the signal.
+ while (!initialized)
+ AllocateAndFree(1);
+ // We call the callback before setting enabled=true on the heap, so we
+ // wait a bit for the assignment to happen.
+ usleep(100000);
+ if (!AHeapProfile_reportSample(heap_id, 0x1, 10u))
+ PERFETTO_FATAL("Expected allocation to be sampled.");
+ AHeapProfile_reportFree(heap_id, 0x1);
+ if (!AHeapProfile_reportSample(heap_id, 0x2, 15u))
+ PERFETTO_FATAL("Expected allocation to be sampled.");
+ if (!AHeapProfile_reportSample(heap_id, 0x3, 15u))
+ PERFETTO_FATAL("Expected allocation to be sampled.");
+ AHeapProfile_reportFree(heap_id, 0x2);
+
+ // Wait around so we can verify it did't crash.
+ for (;;) {
+ }
+}
+
void __attribute__((constructor(1024))) RunReInit() {
const char* a0 = getenv("HEAPPROFD_TESTING_RUN_REINIT_ARG0");
if (a0 == nullptr)
@@ -353,6 +389,57 @@
PERFETTO_FATAL("Should be unreachable");
}
+void __attribute__((constructor(1024))) RunCustomLifetime() {
+ const char* a0 = getenv("HEAPPROFD_TESTING_RUN_LIFETIME_ARG0");
+ const char* a1 = getenv("HEAPPROFD_TESTING_RUN_LIFETIME_ARG1");
+ if (a0 == nullptr)
+ return;
+ uint64_t arg0 = a0 ? base::StringToUInt64(a0).value() : 0;
+ uint64_t arg1 = a0 ? base::StringToUInt64(a1).value() : 0;
+
+ PERFETTO_CHECK(arg1);
+
+ static std::atomic<bool> initialized{false};
+ static std::atomic<bool> disabled{false};
+ static std::atomic<uint64_t> sampling_interval;
+
+ auto enabled_callback = [](void*,
+ const AHeapProfileEnableCallbackInfo* info) {
+ sampling_interval =
+ AHeapProfileEnableCallbackInfo_getSamplingInterval(info);
+ initialized = true;
+ };
+ auto disabled_callback = [](void*, const AHeapProfileDisableCallbackInfo*) {
+ disabled = true;
+ };
+ static uint32_t heap_id =
+ AHeapProfile_registerHeap(AHeapInfo_setDisabledCallback(
+ AHeapInfo_setEnabledCallback(AHeapInfo_create("test"),
+ enabled_callback, nullptr),
+ disabled_callback, nullptr));
+
+ ChildFinishHandshake();
+
+ // heapprofd_client needs malloc to see the signal.
+ while (!initialized)
+ AllocateAndFree(1);
+
+ if (sampling_interval.load() != arg0) {
+ PERFETTO_FATAL("%" PRIu64 " != %" PRIu64, sampling_interval.load(), arg0);
+ }
+
+ while (!disabled)
+ AHeapProfile_reportFree(heap_id, 0x2);
+
+ char x = 'x';
+ PERFETTO_CHECK(base::WriteAll(static_cast<int>(arg1), &x, sizeof(x)) == 1);
+ close(static_cast<int>(arg1));
+
+ // Wait around so we can verify it didn't crash.
+ for (;;) {
+ }
+}
+
std::unique_ptr<TestHelper> GetHelper(base::TestTaskRunner* task_runner) {
std::unique_ptr<TestHelper> helper(new TestHelper(task_runner));
helper->StartServiceIfRequired();
@@ -728,7 +815,7 @@
ValidateSampleSizes(helper.get(), pid, kAllocSize, allocator_name());
}
-TEST_P(HeapprofdEndToEnd, AccurateCustom) {
+TEST_P(HeapprofdEndToEnd, AccurateCustomReportAllocation) {
if (allocator_mode() != AllocatorMode::kCustom)
GTEST_SKIP();
@@ -768,6 +855,46 @@
EXPECT_EQ(total_freed, 25u);
}
+TEST_P(HeapprofdEndToEnd, AccurateCustomReportSample) {
+ if (allocator_mode() != AllocatorMode::kCustom)
+ GTEST_SKIP();
+
+ base::Subprocess child({"/proc/self/exe"});
+ child.args.posix_argv0_override_for_testing = "heapprofd_continuous_malloc";
+ child.args.stdout_mode = base::Subprocess::kDevNull;
+ child.args.stderr_mode = base::Subprocess::kDevNull;
+ child.args.env.push_back("HEAPPROFD_TESTING_RUN_ACCURATE_SAMPLE=1");
+ StartAndWaitForHandshake(&child);
+
+ const uint64_t pid = static_cast<uint64_t>(child.pid());
+
+ TraceConfig trace_config = MakeTraceConfig([pid](HeapprofdConfig* cfg) {
+ cfg->set_sampling_interval_bytes(1000000);
+ cfg->add_pid(pid);
+ cfg->add_heaps("test");
+ });
+
+ auto helper = Trace(trace_config);
+ WRITE_TRACE(helper->full_trace());
+ PrintStats(helper.get());
+ KillAssertRunning(&child);
+
+ ValidateOnlyPID(helper.get(), pid);
+
+ size_t total_alloc = 0;
+ size_t total_freed = 0;
+ for (const protos::gen::TracePacket& packet : helper->trace()) {
+ for (const auto& dump : packet.profile_packet().process_dumps()) {
+ for (const auto& sample : dump.samples()) {
+ total_alloc += sample.self_allocated();
+ total_freed += sample.self_freed();
+ }
+ }
+ }
+ EXPECT_EQ(total_alloc, 40u);
+ EXPECT_EQ(total_freed, 25u);
+}
+
TEST_P(HeapprofdEndToEnd, AccurateDumpAtMaxCustom) {
if (allocator_mode() != AllocatorMode::kCustom)
GTEST_SKIP();
@@ -809,6 +936,47 @@
EXPECT_EQ(total_count, 2u);
}
+TEST_P(HeapprofdEndToEnd, CustomLifetime) {
+ if (allocator_mode() != AllocatorMode::kCustom)
+ GTEST_SKIP();
+
+ int disabled_pipe[2];
+ PERFETTO_CHECK(pipe(disabled_pipe) == 0); // NOLINT(android-cloexec-pipe)
+
+ int disabled_pipe_rd = disabled_pipe[0];
+ int disabled_pipe_wr = disabled_pipe[1];
+
+ base::Subprocess child({"/proc/self/exe"});
+ child.args.posix_argv0_override_for_testing = "heapprofd_continuous_malloc";
+ child.args.stdout_mode = base::Subprocess::kDevNull;
+ child.args.stderr_mode = base::Subprocess::kDevNull;
+ child.args.env.push_back("HEAPPROFD_TESTING_RUN_LIFETIME_ARG0=1000000");
+ child.args.env.push_back("HEAPPROFD_TESTING_RUN_LIFETIME_ARG1=" +
+ std::to_string(disabled_pipe_wr));
+ child.args.preserve_fds.push_back(disabled_pipe_wr);
+ StartAndWaitForHandshake(&child);
+ close(disabled_pipe_wr);
+
+ const uint64_t pid = static_cast<uint64_t>(child.pid());
+
+ TraceConfig trace_config = MakeTraceConfig([pid](HeapprofdConfig* cfg) {
+ cfg->set_sampling_interval_bytes(1000000);
+ cfg->add_pid(pid);
+ cfg->add_heaps("test");
+ });
+
+ auto helper = Trace(trace_config);
+ WRITE_TRACE(helper->full_trace());
+ PrintStats(helper.get());
+ // Give client some time to notice the disconnect.
+ sleep(2);
+ KillAssertRunning(&child);
+
+ char x;
+ EXPECT_EQ(base::Read(disabled_pipe_rd, &x, sizeof(x)), 1);
+ close(disabled_pipe_rd);
+}
+
TEST_P(HeapprofdEndToEnd, TwoProcesses) {
constexpr size_t kAllocSize = 1024;
constexpr size_t kAllocSize2 = 7;
diff --git a/src/profiling/memory/heapprofd_standalone_client_example.cc b/src/profiling/memory/heapprofd_standalone_client_example.cc
index 5e18f4b..5032b4c 100644
--- a/src/profiling/memory/heapprofd_standalone_client_example.cc
+++ b/src/profiling/memory/heapprofd_standalone_client_example.cc
@@ -18,10 +18,18 @@
#include <unistd.h>
+namespace {
+
+void OtherFn(uint32_t heap_id, uint64_t i) {
+ AHeapProfile_reportAllocation(heap_id, i, i);
+}
+
+} // namespace
+
int main(int, char**) {
uint32_t heap_id = AHeapProfile_registerHeap(AHeapInfo_create("test"));
for (uint64_t i = 0; i < 100000; ++i) {
- AHeapProfile_reportAllocation(heap_id, i, i);
+ OtherFn(heap_id, i);
sleep(1);
}
}
diff --git a/src/profiling/memory/malloc_hooks.cc b/src/profiling/memory/malloc_hooks.cc
index 6ba7e79..132f3a8 100644
--- a/src/profiling/memory/malloc_hooks.cc
+++ b/src/profiling/memory/malloc_hooks.cc
@@ -24,6 +24,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/profiling/memory/heap_profile.h"
+#include "src/profiling/memory/heap_profile_internal.h"
#include "src/profiling/memory/wrap_allocators.h"
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index 462c451..ae32ff7 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -75,6 +75,7 @@
using ::testing::_;
using ::testing::Args;
using ::testing::AtLeast;
+using ::testing::DoAll;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::HasSubstr;
diff --git a/test/gtest_logcat_printer.cc b/test/gtest_logcat_printer.cc
index f88e01b..a78fa88 100644
--- a/test/gtest_logcat_printer.cc
+++ b/test/gtest_logcat_printer.cc
@@ -58,8 +58,14 @@
void LogcatPrinter::OnTestEnd(const testing::TestInfo& test_info) {
const auto* result = test_info.result();
const char* state = "N/A";
- if (result)
- state = result->Passed() ? "PASS" : "FAIL";
+ if (result) {
+ if (result->Passed())
+ state = "PASS";
+ else if (result->Skipped())
+ state = "SKIPPED";
+ else if (result->Failed())
+ state = "FAIL";
+ }
PERFETTO_TEST_LOG("Test end: %s.%s [%s]", test_info.test_case_name(),
test_info.name(), state);
}
diff --git a/test/stress_test/stress_test.cc b/test/stress_test/stress_test.cc
index ac53bd3..4fa0d2d 100644
--- a/test/stress_test/stress_test.cc
+++ b/test/stress_test/stress_test.cc
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-#include <signal.h>
#include <stdarg.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
#include <chrono>
#include <list>
@@ -28,7 +25,9 @@
#include <thread>
#include <vector>
+#include "perfetto/base/build_config.h"
#include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/ctrl_c_handler.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/subprocess.h"
@@ -44,6 +43,12 @@
#include "protos/perfetto/trace/test_event.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#else
+#include <signal.h>
+#endif
+
// Generated by gen_configs_blob.py. It defines the kStressTestConfigs array,
// which contains a proto-encoded StressTestConfig message for each .cfg file
// listed in /test/stress_test/configs/BUILD.gn.
@@ -52,6 +57,19 @@
namespace perfetto {
namespace {
+// TODO(primiano): We need a base::File to get around the awkwardness of
+// files on Windows being a mix of int and HANDLE (and open() vs CreateFile())
+// in our codebase.
+base::ScopedPlatformHandle OpenLogFile(const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return base::ScopedPlatformHandle(::CreateFileA(
+ path.c_str(), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr));
+#else
+ return base::OpenFile(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
+#endif
+}
+
using StressTestConfig = protos::gen::StressTestConfig;
struct SigHandlerCtx {
@@ -105,7 +123,11 @@
TestHarness::TestHarness() {
results_dir_ = base::GetSysTempDir() + "/perfetto-stress-test";
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ system(("rmdir \"" + results_dir_ + "\" /s /q").c_str());
+#else
system(("rm -r -- \"" + results_dir_ + "\"").c_str());
+#endif
PERFETTO_CHECK(base::Mkdir(results_dir_));
PERFETTO_LOG("Saving test results in %s", results_dir_.c_str());
}
@@ -125,7 +147,8 @@
log_msg[res++] = '\n';
log_msg[res++] = '\0';
}
- base::ignore_result(write(*error_log_, log_msg, static_cast<size_t>(res)));
+
+ base::WriteAll(*error_log_, log_msg, static_cast<size_t>(res));
}
void TestHarness::RunConfig(const char* cfg_name,
@@ -154,8 +177,7 @@
base::Subprocess traced({bin_dir + "/traced"});
traced.args.env = env_;
if (!verbose) {
- traced.args.out_fd = base::OpenFile(result_dir + "/traced.log",
- O_RDWR | O_CREAT | O_TRUNC, 0644);
+ traced.args.out_fd = OpenLogFile(result_dir + "/traced.log");
traced.args.stderr_mode = traced.args.stdout_mode = base::Subprocess::kFd;
}
traced.Start();
@@ -171,8 +193,7 @@
producer.args.input = cfg.SerializeAsString();
if (!verbose) {
producer.args.out_fd =
- base::OpenFile(result_dir + "/producer." + std::to_string(i) + ".log",
- O_RDWR | O_CREAT | O_TRUNC, 0644);
+ OpenLogFile(result_dir + "/producer." + std::to_string(i) + ".log");
producer.args.stderr_mode = producer.args.stdout_mode =
base::Subprocess::kFd;
}
@@ -190,12 +211,11 @@
consumer.args.env = env_;
consumer.args.input = cfg.trace_config().SerializeAsString();
if (!verbose) {
- consumer.args.out_fd = base::OpenFile(result_dir + "/perfetto.log",
- O_RDWR | O_CREAT | O_TRUNC, 0644);
+ consumer.args.out_fd = OpenLogFile(result_dir + "/perfetto.log");
consumer.args.stderr_mode = consumer.args.stdout_mode =
base::Subprocess::kFd;
}
- unlink(trace_file_path.c_str());
+ remove(trace_file_path.c_str());
consumer.Start();
int64_t t_start = base::GetBootTimeNs().count();
g_sig->pids_to_kill.emplace_back(consumer.pid());
@@ -209,8 +229,6 @@
consumer.KillAndWaitForTermination();
}
- // Stop
- consumer.KillAndWaitForTermination(SIGTERM);
int64_t t_end = base::GetBootTimeNs().count();
for (auto& producer : producers) {
@@ -260,12 +278,14 @@
if (!fd)
return AddFailure("Trace file does not exist");
const off_t file_size = lseek(*fd, 0, SEEK_END);
+ lseek(*fd, 0, SEEK_SET);
if (file_size <= 0)
return AddFailure("Trace file is empty");
+
test_result.trace_size_kb = static_cast<uint32_t>(file_size / 1000);
- const uint8_t* const start = static_cast<const uint8_t*>(mmap(
- nullptr, static_cast<size_t>(file_size), PROT_READ, MAP_PRIVATE, *fd, 0));
- PERFETTO_CHECK(start != MAP_FAILED);
+ std::string trace_data;
+ PERFETTO_CHECK(base::ReadFileDescriptor(*fd, &trace_data));
+ const auto* const start = reinterpret_cast<const uint8_t*>(trace_data.data());
const uint8_t* const end = start + file_size;
constexpr uint8_t kTracePacketTag = MakeTagLengthDelimited(1);
@@ -401,11 +421,17 @@
}
}
-void CtrlCHandler(int) {
+void CtrlCHandler() {
g_sig->aborted.store(true);
for (auto it = g_sig->pids_to_kill.rbegin(); it != g_sig->pids_to_kill.rend();
it++) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ base::ScopedPlatformHandle proc_handle(
+ ::OpenProcess(PROCESS_TERMINATE, false, *it));
+ ::TerminateProcess(*proc_handle, STATUS_CONTROL_C_EXIT);
+#else
kill(*it, SIGKILL);
+#endif
}
}
@@ -425,7 +451,7 @@
}
g_sig = new SigHandlerCtx();
- signal(SIGINT, CtrlCHandler);
+ base::InstallCtrCHandler(&CtrlCHandler);
for (size_t i = 0; i < base::ArraySize(kStressTestConfigs) && !g_sig->aborted;
++i) {
diff --git a/test/trace_processor/profiling/index b/test/trace_processor/profiling/index
index d216ac5..ddb7ab5 100644
--- a/test/trace_processor/profiling/index
+++ b/test/trace_processor/profiling/index
@@ -45,3 +45,7 @@
# perf_sample table (traced_perf trace as an input).
../../data/perf_sample.pb perf_sample.sql perf_sample_perf_sample.out
+
+# this uses llvm-symbolizer to test the offline symbolization built into
+# trace_processor_shell.
+../../data/heapprofd_standalone_client_example-trace stack_profile_symbols.sql stack_profile_symbols.out
diff --git a/test/trace_processor/profiling/stack_profile_symbols.out b/test/trace_processor/profiling/stack_profile_symbols.out
new file mode 100644
index 0000000..64e7397
--- /dev/null
+++ b/test/trace_processor/profiling/stack_profile_symbols.out
@@ -0,0 +1,5 @@
+"name","source_file","line_number"
+"[NULL]","[NULL]",0
+"_start","??",0
+"(anonymous namespace)::OtherFn(unsigned int, unsigned long)","/builds/master/experiment/external/perfetto/out/linux_clang_release/../../src/profiling/memory/heapprofd_standalone_client_example.cc",24
+"main","/builds/master/experiment/external/perfetto/out/linux_clang_release/../../src/profiling/memory/heapprofd_standalone_client_example.cc",32
diff --git a/test/trace_processor/profiling/stack_profile_symbols.sql b/test/trace_processor/profiling/stack_profile_symbols.sql
new file mode 100644
index 0000000..e57f84d
--- /dev/null
+++ b/test/trace_processor/profiling/stack_profile_symbols.sql
@@ -0,0 +1 @@
+select name, source_file, line_number from stack_profile_symbol
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index f7a5dd6..ccc3119 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -35,6 +35,18 @@
from proto_utils import create_message_factory, serialize_textproto_trace, serialize_python_trace
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ENV = {
+ 'PERFETTO_BINARY_PATH': os.path.join(ROOT_DIR, 'test', 'data'),
+}
+if sys.platform.startswith('linux'):
+ ENV['PATH'] = os.path.join(ROOT_DIR, 'buildtools', 'linux64', 'clang', 'bin')
+elif sys.platform.startswith('dawin'):
+ # Sadly, on macOS we need to check out the Android deps to get
+ # llvm symbolizer.
+ ENV['PATH'] = os.path.join(ROOT_DIR, 'buildtools', 'ndk', 'toolchains',
+ 'llvm', 'prebuilt', 'darwin-x86_64', 'bin')
+elif sys.platform.startswith('win32'):
+ ENV['PATH'] = os.path.join(ROOT_DIR, 'buildtools', 'win', 'clang', 'bin')
class Test(object):
@@ -100,7 +112,8 @@
perf_path,
gen_trace_path,
]
- tp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ tp = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=ENV)
(stdout, stderr) = tp.communicate()
if json_output:
@@ -139,7 +152,8 @@
gen_trace_path,
]
- tp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ tp = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=ENV)
(stdout, stderr) = tp.communicate()
return TestResult('query', query_path, gen_trace_path, cmd, expected,
stdout.decode('utf8'), stderr.decode('utf8'), tp.returncode)
diff --git a/tools/install-build-deps b/tools/install-build-deps
index f16263a..d56bba9 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -218,8 +218,8 @@
# Example traces for regression tests.
Dependency(
'test/data.zip',
- 'https://storage.googleapis.com/perfetto/test-data-20201221-112454.zip',
- 'bdb45847b3bfc3f12f10be69e669187e114944ca1ea386a455b0f31d3b1b2c1c',
+ 'https://storage.googleapis.com/perfetto/test-data-20210105-174747.zip',
+ 'e4fe27c8aad5bf0c447b2e853302d5431d0f134e368b73b68e5f56a293945d52',
'all',
),
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index 064994d..2b0f12d 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -24,6 +24,7 @@
} from './scroll_helper';
import {executeSearch} from './search_handler';
+const INSTANT_FOCUS_DURATION_S = 1 / 100;
type Direction = 'Forward'|'Backward';
// Handles all key events than are not handled by the
@@ -174,6 +175,10 @@
if (slice.ts && slice.dur) {
startTs = slice.ts + globals.state.traceTime.startSec;
endTs = startTs + slice.dur;
+ } else if (slice.ts) {
+ startTs = slice.ts - INSTANT_FOCUS_DURATION_S / 2 +
+ globals.state.traceTime.startSec;
+ endTs = startTs + INSTANT_FOCUS_DURATION_S;
}
} else if (selection.kind === 'THREAD_STATE') {
const threadState = globals.threadStateDetails;