Merge "probes_producer: Add inode filemap for /system"
diff --git a/Android.bp b/Android.bp
index 16fefe9..a451202 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,7 @@
"src/base/thread_checker.cc",
"src/base/unix_task_runner.cc",
"src/base/watchdog.cc",
+ "src/ftrace_reader/atrace_wrapper.cc",
"src/ftrace_reader/cpu_reader.cc",
"src/ftrace_reader/event_info.cc",
"src/ftrace_reader/event_info_constants.cc",
@@ -227,6 +228,7 @@
"src/base/thread_checker.cc",
"src/base/unix_task_runner.cc",
"src/base/watchdog.cc",
+ "src/ftrace_reader/atrace_wrapper.cc",
"src/ftrace_reader/cpu_reader.cc",
"src/ftrace_reader/end_to_end_integrationtest.cc",
"src/ftrace_reader/event_info.cc",
@@ -2958,6 +2960,7 @@
"src/base/watchdog.cc",
"src/base/watchdog_unittest.cc",
"src/base/weak_ptr_unittest.cc",
+ "src/ftrace_reader/atrace_wrapper.cc",
"src/ftrace_reader/cpu_reader.cc",
"src/ftrace_reader/cpu_reader_unittest.cc",
"src/ftrace_reader/event_info.cc",
@@ -3120,7 +3123,9 @@
"src/ftrace_reader/ftrace_procfs.cc",
"src/ftrace_reader/proto_translation_table.cc",
"src/traced/probes/probes_producer.cc",
+ "test/fake_producer.cc",
"test/fake_consumer.cc",
+ "test/task_runner_thread.cc",
"test/end_to_end_integrationtest.cc",
],
export_include_dirs: [
@@ -3133,6 +3138,9 @@
"perfetto_src_tracing_ipc",
"libgtest_prod",
],
+ cflags: [
+ "-DPERFETTO_BUILD_WITH_ANDROID",
+ ],
}
cc_library_static {
@@ -3151,4 +3159,7 @@
static_libs: [
"perfetto_src_tracing_ipc",
],
+ cflags: [
+ "-DPERFETTO_BUILD_WITH_ANDROID",
+ ],
}
\ No newline at end of file
diff --git a/Android.bp.extras b/Android.bp.extras
index b882eaa..0b8563f 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -1,5 +1,4 @@
// These targets are appended to the autogenerated Android.bp by tools/gen_android_bp.
-// TODO(lalitm): find a better way to do this sharing.
cc_library_static {
name: "perfetto_cts_deps",
srcs: [
@@ -12,7 +11,9 @@
"src/ftrace_reader/ftrace_procfs.cc",
"src/ftrace_reader/proto_translation_table.cc",
"src/traced/probes/probes_producer.cc",
+ "test/fake_producer.cc",
"test/fake_consumer.cc",
+ "test/task_runner_thread.cc",
"test/end_to_end_integrationtest.cc",
],
export_include_dirs: [
diff --git a/include/perfetto/ftrace_reader/ftrace_controller.h b/include/perfetto/ftrace_reader/ftrace_controller.h
index 3644ec1..f5df1b2 100644
--- a/include/perfetto/ftrace_reader/ftrace_controller.h
+++ b/include/perfetto/ftrace_reader/ftrace_controller.h
@@ -165,7 +165,7 @@
uint32_t drain_period_ms);
static void DrainCPUs(base::WeakPtr<FtraceController>, size_t generation);
- static void UnblockReaders(base::WeakPtr<FtraceController>);
+ static void UnblockReaders(const base::WeakPtr<FtraceController>&);
uint32_t GetDrainPeriodMs();
diff --git a/include/perfetto/tracing/core/trace_config.h b/include/perfetto/tracing/core/trace_config.h
index e57f728..bac3218 100644
--- a/include/perfetto/tracing/core/trace_config.h
+++ b/include/perfetto/tracing/core/trace_config.h
@@ -126,6 +126,11 @@
std::string unknown_fields_;
};
+ enum LockdownModeOperation {
+ LOCKDOWN_UNCHANGED = 0,
+ LOCKDOWN_CLEAR = 1,
+ LOCKDOWN_SET = 2,
+ };
TraceConfig();
~TraceConfig();
TraceConfig(TraceConfig&&) noexcept;
@@ -161,11 +166,17 @@
enable_extra_guardrails_ = value;
}
+ LockdownModeOperation lockdown_mode() const { return lockdown_mode_; }
+ void set_lockdown_mode(LockdownModeOperation value) {
+ lockdown_mode_ = value;
+ }
+
private:
std::vector<BufferConfig> buffers_;
std::vector<DataSource> data_sources_;
uint32_t duration_ms_ = {};
bool enable_extra_guardrails_ = {};
+ LockdownModeOperation lockdown_mode_ = {};
// Allows to preserve unknown protobuf fields for compatibility
// with future versions of .proto files.
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index d35ac8c..bf659ba 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -132,6 +132,15 @@
// and enables guardrails that limit resource usage for traces requested
// by statsd.
optional bool enable_extra_guardrails = 4;
+
+ enum LockdownModeOperation {
+ LOCKDOWN_UNCHANGED = 0;
+ LOCKDOWN_CLEAR = 1;
+ LOCKDOWN_SET = 2;
+ }
+ // Reject producers that are not running under the same UID as the tracing
+ // service.
+ optional LockdownModeOperation lockdown_mode = 5;
}
// End of protos/perfetto/config/trace_config.proto
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index 41349a8..57bc741 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -92,4 +92,13 @@
// and enables guardrails that limit resource usage for traces requested
// by statsd.
optional bool enable_extra_guardrails = 4;
+
+ enum LockdownModeOperation {
+ LOCKDOWN_UNCHANGED = 0;
+ LOCKDOWN_CLEAR = 1;
+ LOCKDOWN_SET = 2;
+ }
+ // Reject producers that are not running under the same UID as the tracing
+ // service.
+ optional LockdownModeOperation lockdown_mode = 5;
}
diff --git a/src/base/page_allocator.cc b/src/base/page_allocator.cc
index ac26c36..86aec38 100644
--- a/src/base/page_allocator.cc
+++ b/src/base/page_allocator.cc
@@ -26,7 +26,7 @@
namespace {
-static constexpr size_t kGuardSize = kPageSize;
+constexpr size_t kGuardSize = kPageSize;
// static
PageAllocator::UniquePtr AllocateInternal(size_t size, bool unchecked) {
diff --git a/src/base/watchdog.cc b/src/base/watchdog.cc
index 97ece02..991e82f 100644
--- a/src/base/watchdog.cc
+++ b/src/base/watchdog.cc
@@ -30,13 +30,13 @@
namespace {
-static constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
+constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
bool IsMultipleOf(uint32_t number, uint32_t divisor) {
return number >= divisor && number % divisor == 0;
}
-double MeanForArray(uint64_t array[], size_t size) {
+double MeanForArray(const uint64_t array[], size_t size) {
uint64_t total = 0;
for (size_t i = 0; i < size; i++) {
total += array[i];
diff --git a/src/base/watchdog_unittest.cc b/src/base/watchdog_unittest.cc
index d383735..3adc261 100644
--- a/src/base/watchdog_unittest.cc
+++ b/src/base/watchdog_unittest.cc
@@ -29,7 +29,8 @@
class TestWatchdog : public Watchdog {
public:
- TestWatchdog(uint32_t polling_interval_ms) : Watchdog(polling_interval_ms) {}
+ explicit TestWatchdog(uint32_t polling_interval_ms)
+ : Watchdog(polling_interval_ms) {}
~TestWatchdog() override {}
TestWatchdog(TestWatchdog&& other) noexcept = default;
};
diff --git a/src/ftrace_reader/BUILD.gn b/src/ftrace_reader/BUILD.gn
index 5fffdb5..29a24b3 100644
--- a/src/ftrace_reader/BUILD.gn
+++ b/src/ftrace_reader/BUILD.gn
@@ -112,6 +112,8 @@
"../protozero",
]
sources = [
+ "atrace_wrapper.cc",
+ "atrace_wrapper.h",
"cpu_reader.cc",
"cpu_reader.h",
"event_info.cc",
diff --git a/src/ftrace_reader/atrace_wrapper.cc b/src/ftrace_reader/atrace_wrapper.cc
new file mode 100644
index 0000000..8ca9b7a
--- /dev/null
+++ b/src/ftrace_reader/atrace_wrapper.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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 "atrace_wrapper.h"
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+
+namespace {
+
+RunAtraceFunction g_run_atrace_for_testing = nullptr;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// Args should include "atrace" for argv[0].
+bool ExecvAtrace(const std::vector<std::string>& args) {
+ int status = 1;
+
+ std::vector<char*> argv;
+ // args, and then a null.
+ argv.reserve(1 + args.size());
+ for (const auto& arg : args)
+ argv.push_back(const_cast<char*>(arg.c_str()));
+ argv.push_back(nullptr);
+
+ pid_t pid = fork();
+ PERFETTO_CHECK(pid >= 0);
+ if (pid == 0) {
+ // Close stdin/out/err + any file descriptor that we might have mistakenly
+ // not marked as FD_CLOEXEC.
+ for (int i = 0; i < 128; i++)
+ close(i);
+ execv("/system/bin/atrace", &argv[0]);
+ // Reached only if execv fails.
+ _exit(1);
+ }
+ PERFETTO_EINTR(waitpid(pid, &status, 0));
+ return status == 0;
+}
+#endif
+
+} // namespace
+
+bool RunAtrace(const std::vector<std::string>& args) {
+ if (g_run_atrace_for_testing)
+ return g_run_atrace_for_testing(args);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ return ExecvAtrace(args);
+#else
+ PERFETTO_LOG("Atrace only supported on Android.");
+ return false;
+#endif
+}
+
+void SetRunAtraceForTesting(RunAtraceFunction f) {
+ g_run_atrace_for_testing = f;
+}
+
+} // namespace perfetto
diff --git a/src/ftrace_reader/atrace_wrapper.h b/src/ftrace_reader/atrace_wrapper.h
new file mode 100644
index 0000000..1984525
--- /dev/null
+++ b/src/ftrace_reader/atrace_wrapper.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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_FTRACE_READER_ATRACE_WRAPPER_H_
+#define SRC_FTRACE_READER_ATRACE_WRAPPER_H_
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace perfetto {
+
+using RunAtraceFunction =
+ std::add_pointer<bool(const std::vector<std::string>& /*args*/)>::type;
+
+bool RunAtrace(const std::vector<std::string>& args);
+void SetRunAtraceForTesting(RunAtraceFunction);
+
+} // namespace perfetto
+
+#endif // SRC_FTRACE_READER_ATRACE_WRAPPER_H_
diff --git a/src/ftrace_reader/cpu_reader.cc b/src/ftrace_reader/cpu_reader.cc
index e3d1a5e..bb617cc 100644
--- a/src/ftrace_reader/cpu_reader.cc
+++ b/src/ftrace_reader/cpu_reader.cc
@@ -157,10 +157,11 @@
}
// static
-void CpuReader::RunWorkerThread(size_t cpu,
- int trace_fd,
- int staging_write_fd,
- std::function<void()> on_data_available) {
+void CpuReader::RunWorkerThread(
+ size_t cpu,
+ int trace_fd,
+ int staging_write_fd,
+ const std::function<void()>& on_data_available) {
// This thread is responsible for moving data from the trace pipe into the
// staging pipe at least one page at a time. This is done using the splice(2)
// system call, which unlike poll/select makes it possible to block until at
diff --git a/src/ftrace_reader/cpu_reader.h b/src/ftrace_reader/cpu_reader.h
index 9379028..d3dbf80 100644
--- a/src/ftrace_reader/cpu_reader.h
+++ b/src/ftrace_reader/cpu_reader.h
@@ -170,7 +170,7 @@
static void RunWorkerThread(size_t cpu,
int trace_fd,
int staging_write_fd,
- std::function<void()> on_data_available);
+ const std::function<void()>& on_data_available);
uint8_t* GetBuffer();
CpuReader(const CpuReader&) = delete;
diff --git a/src/ftrace_reader/cpu_reader_unittest.cc b/src/ftrace_reader/cpu_reader_unittest.cc
index 8340706..5904f5e 100644
--- a/src/ftrace_reader/cpu_reader_unittest.cc
+++ b/src/ftrace_reader/cpu_reader_unittest.cc
@@ -57,14 +57,13 @@
// Round to closest us.
uint64_t actual_us = (actual_ns + kNanoInMicro / 2) / kNanoInMicro;
uint64_t total_expected_us = expected_s * 1000 * 1000 + expected_us;
- if (actual_us == total_expected_us) {
+ if (actual_us == total_expected_us)
return ::testing::AssertionSuccess();
- } else {
- return ::testing::AssertionFailure()
- << actual_ns / kNanoInSecond << "."
- << (actual_ns % kNanoInSecond) / kNanoInMicro << " vs. "
- << expected_s << "." << expected_us;
- }
+
+ return ::testing::AssertionFailure()
+ << actual_ns / kNanoInSecond << "."
+ << (actual_ns % kNanoInSecond) / kNanoInMicro << " vs. " << expected_s
+ << "." << expected_us;
}
// Single class to manage the whole protozero -> scattered stream -> chunks ->
diff --git a/src/ftrace_reader/format_parser.cc b/src/ftrace_reader/format_parser.cc
index 4a265f9..ff516e7 100644
--- a/src/ftrace_reader/format_parser.cc
+++ b/src/ftrace_reader/format_parser.cc
@@ -45,7 +45,7 @@
if (!(std::isalnum(c) || c == '_'))
return false;
}
- return s.size() > 0 && !std::isdigit(s[0]);
+ return !s.empty() && !std::isdigit(s[0]);
}
} // namespace
@@ -140,7 +140,7 @@
return false;
}
- if (!has_id || !has_name || fields.size() == 0) {
+ if (!has_id || !has_name || fields.empty()) {
if (output)
fprintf(stderr, "Could not parse format file: %s.\n",
!has_id ? "no ID found"
diff --git a/src/ftrace_reader/ftrace_config_muxer.cc b/src/ftrace_reader/ftrace_config_muxer.cc
index 7ec1f34..5a1ed14 100644
--- a/src/ftrace_reader/ftrace_config_muxer.cc
+++ b/src/ftrace_reader/ftrace_config_muxer.cc
@@ -16,16 +16,14 @@
#include "ftrace_config_muxer.h"
-#include <fcntl.h>
#include <stdint.h>
#include <string.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
+#include "atrace_wrapper.h"
#include "perfetto/base/utils.h"
#include "proto_translation_table.h"
@@ -47,28 +45,6 @@
return result;
}
-// Including "atrace" for argv[0].
-bool RunAtrace(const std::vector<std::string>& args) {
- int status = 1;
-
- std::vector<char*> argv;
- // args, and then a null.
- argv.reserve(1 + args.size());
- for (const auto& arg : args)
- argv.push_back(const_cast<char*>(arg.c_str()));
- argv.push_back(nullptr);
-
- pid_t pid = fork();
- PERFETTO_CHECK(pid >= 0);
- if (pid == 0) {
- execv("/system/bin/atrace", &argv[0]);
- // Reached only if execv fails.
- _exit(1);
- }
- PERFETTO_EINTR(waitpid(pid, &status, 0));
- return status == 0;
-}
-
} // namespace
std::set<std::string> GetFtraceEvents(const FtraceConfig& request) {
@@ -115,7 +91,7 @@
// If we're about to turn tracing on use this opportunity do some setup:
if (RequiresAtrace(request))
- EnableAtraceOnAndroid(request);
+ EnableAtrace(request);
SetupClock(request);
SetupBufferSize(request);
} else {
@@ -184,7 +160,7 @@
ftrace_->ClearTrace();
current_state_.tracing_on = false;
if (current_state_.atrace_on)
- DisableAtraceOnAndroid();
+ DisableAtrace();
}
return true;
@@ -217,19 +193,11 @@
current_state_.cpu_buffer_size_pages = pages;
}
-void FtraceConfigMuxer::EnableAtraceOnAndroid(const FtraceConfig& request) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
- EnableAtrace(request);
-#else
- PERFETTO_LOG("Atrace only supported on Android.");
-#endif
-}
-
void FtraceConfigMuxer::EnableAtrace(const FtraceConfig& request) {
PERFETTO_DCHECK(!current_state_.atrace_on);
- current_state_.atrace_on = true;
PERFETTO_DLOG("Start atrace...");
+
std::vector<std::string> args;
args.push_back("atrace"); // argv0 for exec()
args.push_back("--async_start");
@@ -241,26 +209,21 @@
args.push_back(app);
}
- PERFETTO_CHECK(RunAtrace(args));
- PERFETTO_DLOG("...done");
-}
+ if (RunAtrace(args))
+ current_state_.atrace_on = true;
-void FtraceConfigMuxer::DisableAtraceOnAndroid() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
- DisableAtrace();
-#else
- PERFETTO_LOG("Atrace only supported on Android.");
-#endif
+ PERFETTO_DLOG("...done");
}
void FtraceConfigMuxer::DisableAtrace() {
- PERFETTO_DCHECK(!current_state_.atrace_on);
+ PERFETTO_DCHECK(current_state_.atrace_on);
PERFETTO_DLOG("Stop atrace...");
- PERFETTO_CHECK(RunAtrace({"atrace", "--async_stop"}));
- PERFETTO_DLOG("...done");
- current_state_.atrace_on = false;
+ if (RunAtrace({"atrace", "--async_stop"}))
+ current_state_.atrace_on = false;
+
+ PERFETTO_DLOG("...done");
}
} // namespace perfetto
diff --git a/src/ftrace_reader/ftrace_config_muxer.h b/src/ftrace_reader/ftrace_config_muxer.h
index 37d031b..c24609c 100644
--- a/src/ftrace_reader/ftrace_config_muxer.h
+++ b/src/ftrace_reader/ftrace_config_muxer.h
@@ -38,7 +38,8 @@
// |RemoveConfig|.
class FtraceConfigMuxer {
public:
- // The ProtoTranslationTable table should outlive this instance.
+ // The FtraceConfigMuxer and ProtoTranslationTable
+ // should outlive this instance.
FtraceConfigMuxer(FtraceProcfs* ftrace, const ProtoTranslationTable* table);
virtual ~FtraceConfigMuxer();
@@ -79,8 +80,6 @@
void SetupClock(const FtraceConfig& request);
void SetupBufferSize(const FtraceConfig& request);
- void EnableAtraceOnAndroid(const FtraceConfig& request);
- void DisableAtraceOnAndroid();
void EnableAtrace(const FtraceConfig& request);
void DisableAtrace();
diff --git a/src/ftrace_reader/ftrace_config_muxer_unittest.cc b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
index f7e3e74..b0ca96c 100644
--- a/src/ftrace_reader/ftrace_config_muxer_unittest.cc
+++ b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
@@ -18,14 +18,18 @@
#include <memory>
-#include "ftrace_procfs.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+
+#include "atrace_wrapper.h"
+#include "ftrace_procfs.h"
#include "proto_translation_table.h"
using testing::_;
using testing::AnyNumber;
using testing::Contains;
+using testing::ElementsAreArray;
+using testing::Eq;
using testing::IsEmpty;
using testing::NiceMock;
using testing::Not;
@@ -52,6 +56,20 @@
MOCK_CONST_METHOD0(NumberOfCpus, size_t());
};
+struct MockRunAtrace {
+ MockRunAtrace() {
+ static MockRunAtrace* instance;
+ instance = this;
+ SetRunAtraceForTesting([](const std::vector<std::string>& args) {
+ return instance->RunAtrace(args);
+ });
+ }
+
+ ~MockRunAtrace() { SetRunAtraceForTesting(nullptr); }
+
+ MOCK_METHOD1(RunAtrace, bool(const std::vector<std::string>&));
+};
+
std::unique_ptr<ProtoTranslationTable> CreateFakeTable() {
std::vector<Field> common_fields;
std::vector<Event> events;
@@ -157,15 +175,10 @@
ASSERT_FALSE(id);
}
-// TODO(hjd): Mock atrace on Android.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_Atrace DISABLED_Atrace
-#else
-#define MAYBE_Atrace Atrace
-#endif
-TEST(FtraceConfigMuxerTest, MAYBE_Atrace) {
+TEST(FtraceConfigMuxerTest, Atrace) {
std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
NiceMock<MockFtraceProcfs> ftrace;
+ MockRunAtrace atrace;
FtraceConfig config = CreateFtraceConfig({"sched_switch"});
*config.add_atrace_categories() = "sched";
@@ -174,6 +187,9 @@
EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
.WillOnce(Return('0'));
+ EXPECT_CALL(atrace,
+ RunAtrace(ElementsAreArray({"atrace", "--async_start", "sched"})))
+ .WillOnce(Return(true));
FtraceConfigId id = model.RequestConfig(config);
ASSERT_TRUE(id);
@@ -183,6 +199,8 @@
EXPECT_THAT(actual_config->ftrace_events(), Contains("sched_switch"));
EXPECT_THAT(actual_config->ftrace_events(), Contains("print"));
+ EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_stop"})))
+ .WillOnce(Return(true));
ASSERT_TRUE(model.RemoveConfig(id));
}
diff --git a/src/ftrace_reader/ftrace_controller.cc b/src/ftrace_reader/ftrace_controller.cc
index 3818507..ccf2022 100644
--- a/src/ftrace_reader/ftrace_controller.cc
+++ b/src/ftrace_reader/ftrace_controller.cc
@@ -26,6 +26,7 @@
#include <array>
#include <string>
+#include <utility>
#include "cpu_reader.h"
#include "event_info.h"
@@ -85,7 +86,6 @@
} // namespace
-// TODO(fmayer): Actually call this on shutdown.
// Method of last resort to reset ftrace state.
// We don't know what state the rest of the system and process is so as far
// as possible avoid allocations.
@@ -184,7 +184,7 @@
// static
void FtraceController::UnblockReaders(
- base::WeakPtr<FtraceController> weak_this) {
+ const base::WeakPtr<FtraceController>& weak_this) {
if (!weak_this)
return;
// Unblock all waiting readers to start moving more data into their
@@ -195,7 +195,7 @@
void FtraceController::StartIfNeeded() {
if (sinks_.size() > 1)
return;
- PERFETTO_CHECK(sinks_.size() != 0);
+ PERFETTO_CHECK(!sinks_.empty());
{
std::unique_lock<std::mutex> lock(lock_);
PERFETTO_CHECK(!listening_for_raw_trace_data_);
@@ -213,7 +213,7 @@
}
uint32_t FtraceController::GetDrainPeriodMs() {
- if (sinks_.size() == 0)
+ if (sinks_.empty())
return kDefaultDrainPeriodMs;
uint32_t min_drain_period_ms = kMaxDrainPeriodMs + 1;
for (const FtraceSink* sink : sinks_) {
@@ -236,7 +236,7 @@
}
void FtraceController::StopIfNeeded() {
- if (sinks_.size() != 0)
+ if (!sinks_.empty())
return;
{
// Unblock any readers that are waiting for us to drain data.
@@ -285,10 +285,11 @@
auto controller_weak = weak_factory_.GetWeakPtr();
auto filter = std::unique_ptr<EventFilter>(new EventFilter(
- *table_.get(), FtraceEventsAsSet(*ftrace_config_muxer_->GetConfig(id))));
+ *table_, FtraceEventsAsSet(*ftrace_config_muxer_->GetConfig(id))));
- auto sink = std::unique_ptr<FtraceSink>(new FtraceSink(
- std::move(controller_weak), id, config, std::move(filter), delegate));
+ auto sink = std::unique_ptr<FtraceSink>(
+ new FtraceSink(std::move(controller_weak), id, std::move(config),
+ std::move(filter), delegate));
Register(sink.get());
return sink;
}
@@ -348,7 +349,7 @@
Delegate* delegate)
: controller_weak_(std::move(controller_weak)),
id_(id),
- config_(config),
+ config_(std::move(config)),
filter_(std::move(filter)),
delegate_(delegate){};
@@ -370,7 +371,7 @@
void FtraceMetadata::AddPid(int32_t pid) {
// Speculative optimization aginst repated pid's while keeping
// faster insertion than a set.
- if (pids.size() && pids.back() == pid)
+ if (!pids.empty() && pids.back() == pid)
return;
pids.push_back(pid);
}
diff --git a/src/ftrace_reader/ftrace_controller_unittest.cc b/src/ftrace_reader/ftrace_controller_unittest.cc
index e0f9b5a..964baff 100644
--- a/src/ftrace_reader/ftrace_controller_unittest.cc
+++ b/src/ftrace_reader/ftrace_controller_unittest.cc
@@ -135,7 +135,7 @@
class MockFtraceProcfs : public FtraceProcfs {
public:
- MockFtraceProcfs(size_t cpu_count = 1) : FtraceProcfs("/root/") {
+ explicit MockFtraceProcfs(size_t cpu_count = 1) : FtraceProcfs("/root/") {
ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(cpu_count));
EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
@@ -260,8 +260,9 @@
std::unique_ptr<MockFtraceProcfs>(new MockFtraceProcfs(cpu_count));
}
- MockFtraceProcfs* raw_procfs = ftrace_procfs.get();
auto model = FakeModel(ftrace_procfs.get(), table.get());
+
+ MockFtraceProcfs* raw_procfs = ftrace_procfs.get();
return std::unique_ptr<TestFtraceController>(new TestFtraceController(
std::move(ftrace_procfs), std::move(table), std::move(model),
std::move(runner), raw_procfs));
diff --git a/src/ftrace_reader/ftrace_procfs.cc b/src/ftrace_reader/ftrace_procfs.cc
index 1eef505..3af2c88 100644
--- a/src/ftrace_reader/ftrace_procfs.cc
+++ b/src/ftrace_reader/ftrace_procfs.cc
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "ftrace_procfs.h"
+#include "src/ftrace_reader/ftrace_procfs.h"
-#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
#include <fstream>
#include <sstream>
@@ -42,25 +42,18 @@
namespace {
-int OpenKmesgFD() {
- // This environment variable gets set to a fd to /dev/kmsg opened for writing.
- // The file gets opened by init as configured in perfetto.rc. We cannot open
- // the file directly due to permissions.
- const char* env = getenv("ANDROID_FILE__dev_kmsg");
- return env != nullptr ? atoi(env) : -1;
-}
-
void KernelLogWrite(const char* s) {
PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
- static int kmesg_fd = OpenKmesgFD();
- if (kmesg_fd != -1) {
- base::ignore_result(write(kmesg_fd, s, strlen(s)));
- }
+ if (FtraceProcfs::g_kmesg_fd != -1)
+ base::ignore_result(write(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
}
} // namespace
// static
+int FtraceProcfs::g_kmesg_fd = -1; // Set by ProbesMain() in probes.cc .
+
+// static
std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
if (!CheckRootPath(root)) {
return nullptr;
@@ -148,11 +141,11 @@
std::string path = root_ + "trace_clock";
std::string s = ReadFileIntoString(path);
- size_t start = s.find("[");
+ size_t start = s.find('[');
if (start == std::string::npos)
return "";
- size_t end = s.find("]", start);
+ size_t end = s.find(']', start);
if (end == std::string::npos)
return "";
@@ -168,7 +161,7 @@
size_t end = 0;
while (true) {
- end = s.find(" ", start);
+ end = s.find(' ', start);
if (end == std::string::npos)
end = s.size();
if (start == end)
@@ -201,7 +194,7 @@
bool FtraceProcfs::WriteToFile(const std::string& path,
const std::string& str) {
- base::ScopedFile fd = base::OpenFile(path.c_str(), O_WRONLY);
+ base::ScopedFile fd = base::OpenFile(path, O_WRONLY);
if (!fd)
return false;
ssize_t written = PERFETTO_EINTR(write(fd.get(), str.c_str(), str.length()));
@@ -214,11 +207,11 @@
base::ScopedFile FtraceProcfs::OpenPipeForCpu(size_t cpu) {
std::string path =
root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace_pipe_raw";
- return base::OpenFile(path.c_str(), O_RDONLY | O_NONBLOCK);
+ return base::OpenFile(path, O_RDONLY | O_NONBLOCK);
}
char FtraceProcfs::ReadOneCharFromFile(const std::string& path) {
- base::ScopedFile fd = base::OpenFile(path.c_str(), O_RDONLY);
+ base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
PERFETTO_CHECK(fd);
char result = '\0';
ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1));
@@ -227,7 +220,7 @@
}
bool FtraceProcfs::ClearFile(const std::string& path) {
- base::ScopedFile fd = base::OpenFile(path.c_str(), O_WRONLY | O_TRUNC);
+ base::ScopedFile fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
return !!fd;
}
diff --git a/src/ftrace_reader/ftrace_procfs.h b/src/ftrace_reader/ftrace_procfs.h
index 10fbda9..faaca88 100644
--- a/src/ftrace_reader/ftrace_procfs.h
+++ b/src/ftrace_reader/ftrace_procfs.h
@@ -28,6 +28,7 @@
class FtraceProcfs {
public:
static std::unique_ptr<FtraceProcfs> Create(const std::string& root);
+ static int g_kmesg_fd;
explicit FtraceProcfs(const std::string& root);
virtual ~FtraceProcfs();
diff --git a/src/ftrace_reader/proto_translation_table.cc b/src/ftrace_reader/proto_translation_table.cc
index 254e710..d5b5ec4 100644
--- a/src/ftrace_reader/proto_translation_table.cc
+++ b/src/ftrace_reader/proto_translation_table.cc
@@ -229,7 +229,7 @@
std::string contents =
ftrace_procfs->ReadEventFormat(event.group, event.name);
FtraceEvent ftrace_event;
- if (contents == "" || !ParseFtraceEvent(contents, &ftrace_event)) {
+ if (contents.empty() || !ParseFtraceEvent(contents, &ftrace_event)) {
continue;
}
diff --git a/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc b/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc
index fa6e178..305f561 100644
--- a/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc
+++ b/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc
@@ -27,7 +27,7 @@
protozero::ContiguousMemoryRange
ScatteredStreamDelegateForTesting::GetNewBuffer() {
PERFETTO_CHECK(writer_);
- if (chunks_.size()) {
+ if (!chunks_.empty()) {
size_t used = chunk_size_ - writer_->bytes_available();
chunks_used_size_.push_back(used);
}
diff --git a/src/ipc/client_impl.cc b/src/ipc/client_impl.cc
index 4ef600b..0560244 100644
--- a/src/ipc/client_impl.cc
+++ b/src/ipc/client_impl.cc
@@ -16,7 +16,9 @@
#include "src/ipc/client_impl.h"
+#include <fcntl.h>
#include <inttypes.h>
+#include <unistd.h>
#include <utility>
@@ -156,6 +158,8 @@
rsize = sock_->Receive(buf.data, buf.size, &fd);
if (fd) {
PERFETTO_DCHECK(!received_fd_);
+ int res = fcntl(*fd, F_SETFD, FD_CLOEXEC);
+ PERFETTO_DCHECK(res == 0);
received_fd_ = std::move(fd);
}
if (!frame_deserializer_.EndReceive(rsize)) {
diff --git a/src/ipc/client_impl_unittest.cc b/src/ipc/client_impl_unittest.cc
index cc98ffe..10f59f9 100644
--- a/src/ipc/client_impl_unittest.cc
+++ b/src/ipc/client_impl_unittest.cc
@@ -151,7 +151,7 @@
method->set_name(method_it.first);
method->set_id(method_it.second->id);
}
- return Reply(reply);
+ Reply(reply);
} else if (req.msg_case() == Frame::kMsgInvokeMethod) {
// Lookup the service and method.
bool has_more = false;
diff --git a/src/ipc/host_impl.cc b/src/ipc/host_impl.cc
index 4d1bfae..c423269 100644
--- a/src/ipc/host_impl.cc
+++ b/src/ipc/host_impl.cc
@@ -123,11 +123,11 @@
void HostImpl::OnReceivedFrame(ClientConnection* client,
const Frame& req_frame) {
- if (req_frame.msg_case() == Frame::kMsgBindService) {
+ if (req_frame.msg_case() == Frame::kMsgBindService)
return OnBindService(client, req_frame);
- } else if (req_frame.msg_case() == Frame::kMsgInvokeMethod) {
+ if (req_frame.msg_case() == Frame::kMsgInvokeMethod)
return OnInvokeMethod(client, req_frame);
- }
+
PERFETTO_DLOG("Received invalid RPC frame %u from client %" PRIu64,
req_frame.msg_case(), client->id);
Frame reply_frame;
diff --git a/src/process_stats/file_utils.cc b/src/process_stats/file_utils.cc
index 633e400..1ddd1c8 100644
--- a/src/process_stats/file_utils.cc
+++ b/src/process_stats/file_utils.cc
@@ -24,7 +24,7 @@
}
void ForEachPidInProcPath(const char* proc_path,
- std::function<void(int)> predicate) {
+ const std::function<void(int)>& predicate) {
DIR* root_dir = opendir(proc_path);
ScopedDir autoclose(root_dir);
struct dirent* child_dir;
@@ -48,7 +48,7 @@
break;
if (rsize == -1 && errno == EINTR)
continue;
- else if (rsize < 0)
+ if (rsize < 0)
return -1;
tot_read += static_cast<size_t>(rsize);
} while (tot_read < length);
diff --git a/src/process_stats/file_utils.h b/src/process_stats/file_utils.h
index a853ecd..9c160da 100644
--- a/src/process_stats/file_utils.h
+++ b/src/process_stats/file_utils.h
@@ -34,7 +34,7 @@
// Invokes predicate(pid) for each folder in |proc_path|/[0-9]+ which has
// a numeric name (typically pids and tids).
void ForEachPidInProcPath(const char* proc_path,
- std::function<void(int)> predicate);
+ const std::function<void(int)>& predicate);
// Reads the contents of |path| fully into |buf| up to |length| chars.
// |buf| is guaranteed to be null terminated.
diff --git a/src/traced/probes/probes.cc b/src/traced/probes/probes.cc
index 78ccea6..f8dea99 100644
--- a/src/traced/probes/probes.cc
+++ b/src/traced/probes/probes.cc
@@ -14,12 +14,16 @@
* limitations under the License.
*/
+#include <fcntl.h>
#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
#include "perfetto/base/logging.h"
#include "perfetto/base/unix_task_runner.h"
#include "perfetto/traced/traced.h"
+#include "src/ftrace_reader/ftrace_procfs.h"
#include "src/traced/probes/probes_producer.h"
namespace perfetto {
@@ -48,6 +52,19 @@
watchdog->Start();
PERFETTO_LOG("Starting %s service", argv[0]);
+
+ // This environment variable is set by Android's init to a fd to /dev/kmsg
+ // opened for writing (see perfetto.rc). We cannot open the file directly
+ // due to permissions.
+ const char* env = getenv("ANDROID_FILE__dev_kmsg");
+ if (env) {
+ FtraceProcfs::g_kmesg_fd = atoi(env);
+ // The file descriptor passed by init doesn't have the FD_CLOEXEC bit set.
+ // Set it so we don't leak this fd while invoking atrace.
+ int res = fcntl(FtraceProcfs::g_kmesg_fd, F_SETFD, FD_CLOEXEC);
+ PERFETTO_DCHECK(res == 0);
+ }
+
base::UnixTaskRunner task_runner;
ProbesProducer producer;
producer.ConnectWithRetries(PERFETTO_PRODUCER_SOCK_NAME, &task_runner);
diff --git a/src/tracing/core/service_impl.cc b/src/tracing/core/service_impl.cc
index 7da32f7..aa53a26 100644
--- a/src/tracing/core/service_impl.cc
+++ b/src/tracing/core/service_impl.cc
@@ -86,6 +86,12 @@
size_t shared_buffer_size_hint_bytes) {
PERFETTO_DCHECK_THREAD(thread_checker_);
+ if (lockdown_mode_ && uid != geteuid()) {
+ PERFETTO_DLOG("Lockdown mode. Rejecting producer with UID %ld",
+ static_cast<unsigned long>(uid));
+ return nullptr;
+ }
+
if (producers_.size() >= kMaxProducerID) {
PERFETTO_DCHECK(false);
return nullptr;
@@ -165,6 +171,10 @@
PERFETTO_DCHECK_THREAD(thread_checker_);
PERFETTO_DLOG("Enabling tracing for consumer %p",
reinterpret_cast<void*>(consumer));
+ if (cfg.lockdown_mode() == TraceConfig::LockdownModeOperation::LOCKDOWN_SET)
+ lockdown_mode_ = true;
+ if (cfg.lockdown_mode() == TraceConfig::LockdownModeOperation::LOCKDOWN_CLEAR)
+ lockdown_mode_ = false;
if (consumer->tracing_session_id_) {
PERFETTO_DLOG(
"A Consumer is trying to EnableTracing() but another tracing session "
@@ -341,7 +351,7 @@
continue;
const uid_t page_owner = tbuf.get_page_owner(page_idx);
uint32_t layout = abi.page_layout_dbg(page_idx);
- size_t num_chunks = abi.GetNumChunksForLayout(layout);
+ size_t num_chunks = SharedMemoryABI::GetNumChunksForLayout(layout);
for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
if (abi.GetChunkState(page_idx, chunk_idx) ==
SharedMemoryABI::kChunkFree) {
@@ -516,6 +526,12 @@
PERFETTO_DCHECK_THREAD(thread_checker_);
ProducerEndpointImpl* producer = GetProducer(data_source.producer_id);
PERFETTO_DCHECK(producer);
+ // An existing producer that is not ftrace could have registered itself as
+ // ftrace, we must not enable it in that case.
+ if (lockdown_mode_ && producer->uid_ != getuid()) {
+ PERFETTO_DLOG("Lockdown mode: not enabling producer %hu", producer->id_);
+ return;
+ }
// TODO(primiano): match against |producer_name_filter| and add tests
// for registration ordering (data sources vs consumers).
diff --git a/src/tracing/core/service_impl.h b/src/tracing/core/service_impl.h
index 2717d2d..eae6364 100644
--- a/src/tracing/core/service_impl.h
+++ b/src/tracing/core/service_impl.h
@@ -262,6 +262,8 @@
std::map<TracingSessionID, TracingSession> tracing_sessions_;
std::map<BufferID, TraceBuffer> buffers_;
+ bool lockdown_mode_ = false;
+
PERFETTO_THREAD_CHECKER(thread_checker_)
base::WeakPtrFactory<ServiceImpl> weak_ptr_factory_; // Keep at the end.
diff --git a/src/tracing/core/service_impl_unittest.cc b/src/tracing/core/service_impl_unittest.cc
index 7c9f0a4..043eae2 100644
--- a/src/tracing/core/service_impl_unittest.cc
+++ b/src/tracing/core/service_impl_unittest.cc
@@ -174,6 +174,49 @@
Mock::VerifyAndClearExpectations(&mock_consumer);
}
+TEST_F(ServiceImplTest, LockdownMode) {
+ MockConsumer mock_consumer;
+ EXPECT_CALL(mock_consumer, OnConnect());
+ std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
+ svc->ConnectConsumer(&mock_consumer);
+
+ TraceConfig trace_config;
+ trace_config.set_lockdown_mode(
+ TraceConfig::LockdownModeOperation::LOCKDOWN_SET);
+ consumer_endpoint->EnableTracing(trace_config);
+ task_runner.RunUntilIdle();
+
+ InSequence seq;
+
+ MockProducer mock_producer;
+ std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
+ svc->ConnectProducer(&mock_producer, geteuid() + 1 /* uid */);
+
+ MockProducer mock_producer_sameuid;
+ std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_sameuid =
+ svc->ConnectProducer(&mock_producer_sameuid, geteuid() /* uid */);
+
+ EXPECT_CALL(mock_producer, OnConnect()).Times(0);
+ EXPECT_CALL(mock_producer_sameuid, OnConnect());
+ task_runner.RunUntilIdle();
+
+ Mock::VerifyAndClearExpectations(&mock_producer);
+
+ consumer_endpoint->DisableTracing();
+ task_runner.RunUntilIdle();
+
+ trace_config.set_lockdown_mode(
+ TraceConfig::LockdownModeOperation::LOCKDOWN_CLEAR);
+ consumer_endpoint->EnableTracing(trace_config);
+ task_runner.RunUntilIdle();
+
+ EXPECT_CALL(mock_producer, OnConnect());
+ producer_endpoint_sameuid =
+ svc->ConnectProducer(&mock_producer, geteuid() + 1);
+
+ task_runner.RunUntilIdle();
+}
+
TEST_F(ServiceImplTest, DisconnectConsumerWhileTracing) {
MockProducer mock_producer;
std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
diff --git a/src/tracing/core/trace_config.cc b/src/tracing/core/trace_config.cc
index f17663f..7c397a7 100644
--- a/src/tracing/core/trace_config.cc
+++ b/src/tracing/core/trace_config.cc
@@ -61,6 +61,10 @@
"size mismatch");
enable_extra_guardrails_ = static_cast<decltype(enable_extra_guardrails_)>(
proto.enable_extra_guardrails());
+
+ static_assert(sizeof(lockdown_mode_) == sizeof(proto.lockdown_mode()),
+ "size mismatch");
+ lockdown_mode_ = static_cast<decltype(lockdown_mode_)>(proto.lockdown_mode());
unknown_fields_ = proto.unknown_fields();
}
@@ -88,6 +92,11 @@
proto->set_enable_extra_guardrails(
static_cast<decltype(proto->enable_extra_guardrails())>(
enable_extra_guardrails_));
+
+ static_assert(sizeof(lockdown_mode_) == sizeof(proto->lockdown_mode()),
+ "size mismatch");
+ proto->set_lockdown_mode(
+ static_cast<decltype(proto->lockdown_mode())>(lockdown_mode_));
*(proto->mutable_unknown_fields()) = unknown_fields_;
}
diff --git a/test/cts/Android.bp b/test/cts/Android.bp
index 18395f1..f22c193 100644
--- a/test/cts/Android.bp
+++ b/test/cts/Android.bp
@@ -5,16 +5,16 @@
],
static_libs: [
"libgmock",
- "libgtest",
- "libgtest_main",
"libprotobuf-cpp-lite",
"perfetto_src_tracing_ipc",
- "perfetto_cts_deps",
],
shared_libs: [
"libandroid",
"liblog",
],
+ whole_static_libs: [
+ "perfetto_cts_deps"
+ ],
test_suites: [
"cts",
"vts",
diff --git a/test/cts/AndroidTest.xml b/test/cts/AndroidTest.xml
index 38b6b72..05b0d80 100644
--- a/test/cts/AndroidTest.xml
+++ b/test/cts/AndroidTest.xml
@@ -24,6 +24,7 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="CtsPerfettoTestCases->/data/local/tmp/CtsPerfettoTestCases" />
+ <option name="append-bitness" value="true" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="am start -n android.perfetto.producer/.ProducerActivity" />