blob: cd2326a9a7c22763daee1389b0f9ee28ba8253f0 [file] [log] [blame]
/*
* 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 "src/trace_processor/proto_trace_parser.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/trace_processor/blob_reader.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/sched_tracker.h"
#include "perfetto/trace/trace.pb.h"
#include "perfetto/trace/trace_packet.pb.h"
namespace perfetto {
namespace trace_processor {
namespace {
using ::testing::_;
using ::testing::Args;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::Pointwise;
class FakeStringBlobReader : public BlobReader {
public:
FakeStringBlobReader(const std::string& data) : data_(data) {}
~FakeStringBlobReader() override {}
uint32_t Read(uint64_t offset, uint32_t len, uint8_t* dst) override {
PERFETTO_CHECK(offset <= data_.size());
uint32_t rsize =
std::min(static_cast<uint32_t>(data_.size() - offset), len);
memcpy(dst, data_.c_str() + offset, rsize);
return rsize;
}
private:
std::string data_;
};
class MockSchedTracker : public SchedTracker {
public:
MockSchedTracker(TraceProcessorContext* context) : SchedTracker(context) {}
virtual ~MockSchedTracker() = default;
MOCK_METHOD6(PushSchedSwitch,
void(uint32_t cpu,
uint64_t timestamp,
uint32_t prev_pid,
uint32_t prev_state,
base::StringView prev_comm,
uint32_t next_pid));
};
class MockProcessTracker : public ProcessTracker {
public:
MockProcessTracker(TraceProcessorContext* context)
: ProcessTracker(context) {}
MOCK_METHOD2(UpdateProcess,
UniquePid(uint32_t pid, base::StringView process_name));
MOCK_METHOD2(UpdateThread, UniqueTid(uint32_t tid, uint32_t tgid));
};
TEST(ProtoTraceParser, LoadSingleEvent) {
protos::Trace trace;
auto* bundle = trace.add_packet()->mutable_ftrace_events();
bundle->set_cpu(10);
auto* event = bundle->add_event();
event->set_timestamp(1000);
static const char kProcName[] = "proc1";
auto* sched_switch = event->mutable_sched_switch();
sched_switch->set_prev_pid(10);
sched_switch->set_prev_state(32);
sched_switch->set_prev_comm(kProcName);
sched_switch->set_next_pid(100);
TraceProcessorContext context;
MockSchedTracker* sched = new MockSchedTracker(&context);
context.sched_tracker.reset(sched);
EXPECT_CALL(*sched, PushSchedSwitch(10, 1000, 10, 32,
base::StringView(kProcName), 100));
FakeStringBlobReader reader(trace.SerializeAsString());
ProtoTraceParser parser(&reader, &context);
parser.ParseNextChunk();
}
TEST(ProtoTraceParser, LoadMultipleEvents) {
protos::Trace trace;
auto* bundle = trace.add_packet()->mutable_ftrace_events();
bundle->set_cpu(10);
auto* event = bundle->add_event();
event->set_timestamp(1000);
static const char kProcName1[] = "proc1";
auto* sched_switch = event->mutable_sched_switch();
sched_switch->set_prev_pid(10);
sched_switch->set_prev_state(32);
sched_switch->set_prev_comm(kProcName1);
sched_switch->set_next_pid(100);
event = bundle->add_event();
event->set_timestamp(1001);
static const char kProcName2[] = "proc2";
sched_switch = event->mutable_sched_switch();
sched_switch->set_prev_pid(100);
sched_switch->set_prev_state(32);
sched_switch->set_prev_comm(kProcName2);
sched_switch->set_next_pid(10);
TraceProcessorContext context;
MockSchedTracker* sched = new MockSchedTracker(&context);
context.sched_tracker.reset(sched);
EXPECT_CALL(*sched, PushSchedSwitch(10, 1000, 10, 32,
base::StringView(kProcName1), 100));
EXPECT_CALL(*sched, PushSchedSwitch(10, 1001, 100, 32,
base::StringView(kProcName2), 10));
FakeStringBlobReader reader(trace.SerializeAsString());
ProtoTraceParser parser(&reader, &context);
parser.ParseNextChunk();
}
TEST(ProtoTraceParser, LoadMultiplePackets) {
protos::Trace trace;
auto* bundle = trace.add_packet()->mutable_ftrace_events();
bundle->set_cpu(10);
auto* event = bundle->add_event();
event->set_timestamp(1000);
static const char kProcName1[] = "proc1";
auto* sched_switch = event->mutable_sched_switch();
sched_switch->set_prev_pid(10);
sched_switch->set_prev_state(32);
sched_switch->set_prev_comm(kProcName1);
sched_switch->set_next_pid(100);
bundle = trace.add_packet()->mutable_ftrace_events();
bundle->set_cpu(10);
event = bundle->add_event();
event->set_timestamp(1001);
static const char kProcName2[] = "proc2";
sched_switch = event->mutable_sched_switch();
sched_switch->set_prev_pid(100);
sched_switch->set_prev_state(32);
sched_switch->set_prev_comm(kProcName2);
sched_switch->set_next_pid(10);
TraceProcessorContext context;
MockSchedTracker* sched = new MockSchedTracker(&context);
context.sched_tracker.reset(sched);
EXPECT_CALL(*sched, PushSchedSwitch(10, 1000, 10, 32,
base::StringView(kProcName1), 100));
EXPECT_CALL(*sched, PushSchedSwitch(10, 1001, 100, 32,
base::StringView(kProcName2), 10));
FakeStringBlobReader reader(trace.SerializeAsString());
ProtoTraceParser parser(&reader, &context);
parser.ParseNextChunk();
}
TEST(ProtoTraceParser, RepeatedLoadSinglePacket) {
protos::Trace trace;
auto* bundle = trace.add_packet()->mutable_ftrace_events();
bundle->set_cpu(10);
auto* event = bundle->add_event();
event->set_timestamp(1000);
static const char kProcName1[] = "proc1";
auto* sched_switch = event->mutable_sched_switch();
sched_switch->set_prev_pid(10);
sched_switch->set_prev_state(32);
sched_switch->set_prev_comm(kProcName1);
sched_switch->set_next_pid(100);
// Make the chunk size the size of the first packet.
uint32_t chunk_size = static_cast<uint32_t>(trace.ByteSize());
bundle = trace.add_packet()->mutable_ftrace_events();
bundle->set_cpu(10);
event = bundle->add_event();
event->set_timestamp(1001);
static const char kProcName2[] = "proc2";
sched_switch = event->mutable_sched_switch();
sched_switch->set_prev_pid(100);
sched_switch->set_prev_state(32);
sched_switch->set_prev_comm(kProcName2);
sched_switch->set_next_pid(10);
TraceProcessorContext context;
MockSchedTracker* sched = new MockSchedTracker(&context);
context.sched_tracker.reset(sched);
EXPECT_CALL(*sched, PushSchedSwitch(10, 1000, 10, 32,
base::StringView(kProcName1), 100));
FakeStringBlobReader reader(trace.SerializeAsString());
ProtoTraceParser parser(&reader, &context);
parser.set_chunk_size_for_testing(chunk_size);
parser.ParseNextChunk();
EXPECT_CALL(*sched, PushSchedSwitch(10, 1001, 100, 32,
base::StringView(kProcName2), 10));
parser.ParseNextChunk();
}
TEST(ProtoTraceParserTest, LoadProcessPacket) {
protos::Trace trace;
auto* tree = trace.add_packet()->mutable_process_tree();
auto* process = tree->add_processes();
static const char kProcName1[] = "proc1";
process->add_cmdline(kProcName1);
process->set_pid(1);
process->set_ppid(2);
TraceProcessorContext context;
MockProcessTracker* process_tracker = new MockProcessTracker(&context);
context.process_tracker.reset(process_tracker);
EXPECT_CALL(*process_tracker, UpdateProcess(1, base::StringView(kProcName1)));
FakeStringBlobReader reader(trace.SerializeAsString());
ProtoTraceParser parser(&reader, &context);
parser.ParseNextChunk();
}
TEST(ProtoTraceParserTest, LoadProcessPacket_FirstCmdline) {
protos::Trace trace;
auto* tree = trace.add_packet()->mutable_process_tree();
auto* process = tree->add_processes();
static const char kProcName1[] = "proc1";
static const char kProcName2[] = "proc2";
process->add_cmdline(kProcName1);
process->add_cmdline(kProcName2);
process->set_pid(1);
process->set_ppid(2);
TraceProcessorContext context;
MockProcessTracker* process_tracker = new MockProcessTracker(&context);
context.process_tracker.reset(process_tracker);
EXPECT_CALL(*process_tracker, UpdateProcess(1, base::StringView(kProcName1)));
FakeStringBlobReader reader(trace.SerializeAsString());
ProtoTraceParser parser(&reader, &context);
parser.ParseNextChunk();
}
TEST(ProtoTraceParserTest, LoadThreadPacket) {
protos::Trace trace;
auto* tree = trace.add_packet()->mutable_process_tree();
auto* thread = tree->add_threads();
thread->set_tid(1);
thread->set_tgid(2);
TraceProcessorContext context;
MockProcessTracker* process_tracker = new MockProcessTracker(&context);
context.process_tracker.reset(process_tracker);
EXPECT_CALL(*process_tracker, UpdateThread(1, 2));
FakeStringBlobReader reader(trace.SerializeAsString());
ProtoTraceParser parser(&reader, &context);
parser.ParseNextChunk();
}
} // namespace
} // namespace trace_processor
} // namespace perfetto