blob: ead03182f6ad88d882dab268e10771b94c3308e4 [file]
/*
* 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.
*/
#include <string_view>
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/subprocess.h"
#include "perfetto/ext/base/temp_file.h"
#include "perfetto/ext/base/utils.h"
#include "protos/perfetto/trace_processor/trace_processor.gen.h"
#include "test/gtest_and_gmock.h"
#include "test/test_helper.h"
namespace perfetto::trace_processor {
namespace {
using TraceProcessorRpc = protos::gen::TraceProcessorRpc;
using TraceProcessorRpcStream = protos::gen::TraceProcessorRpcStream;
using CellsBatch = protos::gen::QueryResult::CellsBatch;
using testing::AllOf;
using testing::ElementsAre;
using testing::HasSubstr;
using testing::IsEmpty;
using testing::Property;
using testing::SizeIs;
const std::string_view kSimpleSystrace = R"(# tracer
surfaceflinger-598 ( 598) [004] .... 10852.771242: tracing_mark_write: B|598|some event
surfaceflinger-598 ( 598) [004] .... 10852.771245: tracing_mark_write: E|598
)";
std::string ShellPath() {
return base::GetCurExecutableDir() + "/trace_processor_shell";
}
// Writes kSimpleSystrace to a temp file and returns the TempFile object.
base::TempFile WriteSimpleSystrace() {
auto f = base::TempFile::Create();
base::WriteAll(f.fd(), kSimpleSystrace.data(),
static_cast<size_t>(kSimpleSystrace.size()));
return f;
}
// Writes arbitrary content to a temp file and returns the TempFile object.
base::TempFile WriteTempFile(const std::string& content) {
auto f = base::TempFile::Create();
base::WriteAll(f.fd(), content.data(), content.size());
return f;
}
struct SubprocessResult {
int exit_code;
std::string out;
};
// Runs trace_processor_shell with the given args. stdout and stderr are both
// captured into `out`.
SubprocessResult RunShell(std::initializer_list<std::string> extra_args) {
base::Subprocess p;
p.args.exec_cmd.push_back(ShellPath());
for (const auto& a : extra_args) {
p.args.exec_cmd.push_back(a);
}
p.args.stdin_mode = base::Subprocess::InputMode::kDevNull;
p.args.stdout_mode = base::Subprocess::OutputMode::kBuffer;
p.args.stderr_mode = base::Subprocess::OutputMode::kBuffer;
p.Start();
PERFETTO_CHECK(p.Wait(kDefaultTestTimeoutMs));
return {p.returncode(), std::move(p.output())};
}
// ---------------------------------------------------------------------------
// Classic CLI backcompat tests
// ---------------------------------------------------------------------------
TEST(TraceProcessorShellIntegrationTest, ClassicVersion) {
auto result = RunShell({"-v"});
EXPECT_EQ(result.exit_code, 0);
EXPECT_THAT(result.out, HasSubstr("Trace Processor RPC API version"));
}
TEST(TraceProcessorShellIntegrationTest, ClassicHelp) {
auto result = RunShell({"-h"});
EXPECT_EQ(result.exit_code, 0);
EXPECT_THAT(result.out, HasSubstr("Interactive trace processor shell"));
}
TEST(TraceProcessorShellIntegrationTest, ClassicUnknownFlag) {
auto result = RunShell({"--nonexistent"});
EXPECT_NE(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicQueryString) {
auto trace = WriteSimpleSystrace();
auto result = RunShell({"-Q", "SELECT 1 AS x", trace.path()});
EXPECT_EQ(result.exit_code, 0);
EXPECT_THAT(result.out, HasSubstr("x"));
EXPECT_THAT(result.out, HasSubstr("1"));
}
TEST(TraceProcessorShellIntegrationTest, ClassicQueryFile) {
auto trace = WriteSimpleSystrace();
auto query = WriteTempFile("SELECT 42 AS val;");
auto result = RunShell({"-q", query.path(), trace.path()});
EXPECT_EQ(result.exit_code, 0);
EXPECT_THAT(result.out, HasSubstr("val"));
EXPECT_THAT(result.out, HasSubstr("42"));
}
TEST(TraceProcessorShellIntegrationTest, ClassicQueryStringNoTrace) {
auto result = RunShell({"-Q", "SELECT 1"});
EXPECT_NE(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicQueryFileBadPath) {
auto trace = WriteSimpleSystrace();
auto result = RunShell({"-q", "/nonexistent.sql", trace.path()});
EXPECT_NE(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicFullSort) {
auto trace = WriteSimpleSystrace();
auto result = RunShell({"--full-sort", "-Q", "SELECT 1", trace.path()});
EXPECT_EQ(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicNoFtraceRaw) {
auto trace = WriteSimpleSystrace();
auto result = RunShell({"--no-ftrace-raw", "-Q", "SELECT 1", trace.path()});
EXPECT_EQ(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicCropTrackEvents) {
auto trace = WriteSimpleSystrace();
auto result =
RunShell({"--crop-track-events", "-Q", "SELECT 1", trace.path()});
EXPECT_EQ(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicAnalyzeProtoContent) {
auto trace = WriteSimpleSystrace();
auto result = RunShell(
{"--analyze-trace-proto-content", "-Q", "SELECT 1", trace.path()});
EXPECT_EQ(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicExport) {
auto trace = WriteSimpleSystrace();
auto out_db = base::TempFile::Create();
auto result = RunShell({"-e", out_db.path(), trace.path()});
EXPECT_EQ(result.exit_code, 0);
EXPECT_TRUE(base::FileExists(out_db.path()));
}
TEST(TraceProcessorShellIntegrationTest, ClassicSummary) {
auto trace = WriteSimpleSystrace();
auto result = RunShell({"--summary", trace.path()});
EXPECT_EQ(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicDev) {
auto trace = WriteSimpleSystrace();
auto result = RunShell({"--dev", "-Q", "SELECT 1", trace.path()});
EXPECT_EQ(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicExtraChecks) {
auto trace = WriteSimpleSystrace();
auto result = RunShell({"--extra-checks", "-Q", "SELECT 1", trace.path()});
EXPECT_EQ(result.exit_code, 0);
}
TEST(TraceProcessorShellIntegrationTest, ClassicSummaryAndMetricsConflict) {
auto trace = WriteSimpleSystrace();
auto result =
RunShell({"--summary", "--run-metrics", "android_cpu", trace.path()});
EXPECT_NE(result.exit_code, 0);
}
// ---------------------------------------------------------------------------
// Existing RPC test
// ---------------------------------------------------------------------------
TEST(TraceProcessorShellIntegrationTest, StdioSimpleRequestResponse) {
TraceProcessorRpcStream req;
auto* rpc = req.add_msg();
rpc->set_append_trace_data(kSimpleSystrace.data(), kSimpleSystrace.size());
rpc->set_request(TraceProcessorRpc::TPM_APPEND_TRACE_DATA);
rpc = req.add_msg();
rpc->set_request(TraceProcessorRpc::TPM_FINALIZE_TRACE_DATA);
rpc = req.add_msg();
rpc->set_request(TraceProcessorRpc::TPM_QUERY_STREAMING);
rpc->mutable_query_args()->set_sql_query("SELECT ts, dur FROM slice");
base::Subprocess process(
{base::GetCurExecutableDir() + "/trace_processor_shell", "--stdiod"});
process.args.stdin_mode = base::Subprocess::InputMode::kBuffer;
process.args.stdout_mode = base::Subprocess::OutputMode::kBuffer;
process.args.stderr_mode = base::Subprocess::OutputMode::kInherit;
process.args.input = req.SerializeAsString();
process.Start();
ASSERT_TRUE(process.Wait(kDefaultTestTimeoutMs));
TraceProcessorRpcStream stream;
stream.ParseFromString(process.output());
ASSERT_THAT(stream.msg(),
ElementsAre(Property(&TraceProcessorRpc::response,
TraceProcessorRpc::TPM_APPEND_TRACE_DATA),
Property(&TraceProcessorRpc::response,
TraceProcessorRpc::TPM_FINALIZE_TRACE_DATA),
Property(&TraceProcessorRpc::response,
TraceProcessorRpc::TPM_QUERY_STREAMING)));
ASSERT_THAT(stream.msg()[0].append_result().error(), IsEmpty());
ASSERT_THAT(stream.msg()[2].query_result().batch(), SizeIs(1));
ASSERT_THAT(stream.msg()[2].query_result().batch()[0].varint_cells(),
ElementsAre(10852771242000, 3000));
}
} // namespace
} // namespace perfetto::trace_processor