| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "tracing.h" |
| |
| #include <lib/zircon-internal/ktrace.h> |
| |
| #include <fstream> |
| #include <string> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <re2/re2.h> |
| |
| using ::testing::Invoke; |
| using ::testing::Return; |
| |
| namespace { |
| class MockTracing : public Tracing { |
| public: |
| MOCK_METHOD(void, ReadKernelBuffer, |
| (zx_handle_t handle, void* data_buf, uint32_t offset, size_t len, size_t* bytes_read), |
| (override)); |
| |
| MOCK_METHOD((std::tuple<bool, bool>), FetchRecord, |
| (zx_handle_t handle, uint8_t* data_buf, uint32_t* offset, size_t* bytes_read, |
| size_t buf_len), |
| (override)); |
| |
| std::tuple<bool, bool> RealFetchRecord(zx_handle_t handle, uint8_t* data_buf, uint32_t* offset, |
| size_t* bytes_read, size_t buf_len) { |
| return Tracing::FetchRecord(handle, data_buf, offset, bytes_read, buf_len); |
| } |
| }; |
| |
| class CreateMockTrace { |
| public: |
| CreateMockTrace(uint32_t tag, uint32_t tid, uint64_t ts) { |
| tag_ = tag; |
| tid_ = tid; |
| ts_ = ts; |
| } |
| |
| std::tuple<bool, bool> operator()(zx_handle_t, uint8_t* data_buf, uint32_t*, size_t*, size_t) { |
| ktrace_header_t* record = reinterpret_cast<ktrace_header_t*>(data_buf); |
| |
| record->tag = tag_; |
| record->tid = tid_; |
| record->ts = ts_; |
| |
| return {true, false}; |
| } |
| |
| private: |
| uint32_t tag_; |
| uint32_t tid_; |
| uint64_t ts_; |
| }; |
| |
| class CreateMockNameTrace { |
| public: |
| CreateMockNameTrace(uint32_t tag, uint32_t tid, uint64_t ts, uint32_t name_id, |
| const char* expected_string_ref) { |
| tag_ = tag; |
| tid_ = tid; |
| ts_ = ts; |
| name_id_ = name_id; |
| expected_string_ref_ = expected_string_ref; |
| } |
| |
| std::tuple<bool, bool> operator()(zx_handle_t, uint8_t* data_buf, uint32_t*, size_t*, size_t) { |
| ktrace_header_t* record = reinterpret_cast<ktrace_header_t*>(data_buf); |
| |
| record->tag = tag_; |
| record->tid = tid_; |
| record->ts = ts_; |
| |
| const uint32_t string_ref_len = |
| static_cast<uint32_t>(strnlen(expected_string_ref_, ZX_MAX_NAME_LEN - 1)); |
| ktrace_rec_name_t* name_record = reinterpret_cast<ktrace_rec_name_t*>(record); |
| |
| name_record->id = name_id_; |
| memcpy(name_record->name, expected_string_ref_, string_ref_len); |
| name_record->name[string_ref_len] = 0; |
| |
| return {true, false}; |
| } |
| |
| private: |
| uint32_t tag_; |
| uint32_t tid_; |
| uint64_t ts_; |
| uint32_t name_id_; |
| const char* expected_string_ref_; |
| }; |
| |
| class CreateMockTrace128BitPayload { |
| public: |
| CreateMockTrace128BitPayload(uint32_t tag, uint32_t tid, uint64_t ts, uint64_t a, uint64_t b) { |
| tag_ = tag; |
| tid_ = tid; |
| ts_ = ts; |
| a_ = a; |
| b_ = b; |
| } |
| |
| std::tuple<bool, bool> operator()(zx_handle_t, uint8_t* data_buf, uint32_t*, size_t*, size_t) { |
| ktrace_header_t* record = reinterpret_cast<ktrace_header_t*>(data_buf); |
| |
| record->tag = tag_; |
| record->tid = tid_; |
| record->ts = ts_; |
| |
| void* const payload = record + 1; |
| uint64_t* const args = static_cast<uint64_t*>(payload); |
| |
| args[0] = a_; |
| args[1] = b_; |
| |
| return {true, false}; |
| } |
| |
| private: |
| uint32_t tag_; |
| uint32_t tid_; |
| uint64_t ts_; |
| uint64_t a_; |
| uint64_t b_; |
| }; |
| |
| class ReaderHelper { |
| public: |
| void ReadZeroBytes(zx_handle_t, void*, uint32_t, size_t, size_t* bytes_read) { *bytes_read = 0; } |
| std::tuple<bool, bool> ReturnEndOfKernelBuffer(zx_handle_t, uint8_t*, uint32_t*, size_t*, |
| size_t) { |
| return {true, true}; |
| } |
| }; |
| |
| class CreateMockKernelRead { |
| public: |
| CreateMockKernelRead(uint32_t tag, uint32_t tid, uint64_t ts) { |
| tag_ = tag; |
| tid_ = tid; |
| ts_ = ts; |
| } |
| |
| void operator()(zx_handle_t, void* data_buf, uint32_t, size_t len, size_t* bytes_read) { |
| ktrace_header_t record = { |
| .tag = tag_, |
| .tid = tid_, |
| .ts = ts_, |
| }; |
| |
| if (len < KTRACE_HDRSIZE) |
| return; |
| |
| ktrace_header_t* record_ptr = reinterpret_cast<ktrace_header_t*>(data_buf); |
| *record_ptr = record; |
| |
| *bytes_read = sizeof(record); |
| } |
| |
| private: |
| uint32_t tag_; |
| uint32_t tid_; |
| uint64_t ts_; |
| }; |
| |
| std::ofstream OpenFile(std::string tracing_filepath) { |
| std::ofstream file; |
| file.open(tracing_filepath); |
| return file; |
| } |
| |
| TEST(TracingTest, StartSetsRunningToTrue) { |
| Tracing tracing; |
| |
| tracing.Start(KTRACE_GRP_ALL); |
| ASSERT_TRUE(tracing.running()); |
| } |
| |
| TEST(TracingTest, StopSetsRunningToFalse) { |
| Tracing tracing; |
| |
| tracing.Stop(); |
| ASSERT_FALSE(tracing.running()); |
| } |
| |
| TEST(TracingTest, DestructorStopsTracing) { |
| Tracing tracing; |
| |
| tracing.Start(KTRACE_GRP_ALL); |
| EXPECT_TRUE(tracing.running()); |
| |
| tracing.~Tracing(); |
| |
| ASSERT_FALSE(tracing.running()); |
| } |
| |
| TEST(TracingTest, BasicWriteSucceeds) { |
| Tracing tracing; |
| |
| std::ofstream human_readable_file = OpenFile("/tmp/unittest.ktrace"); |
| ASSERT_TRUE(human_readable_file); |
| |
| ASSERT_TRUE(tracing.WriteHumanReadable(human_readable_file)); |
| human_readable_file.close(); |
| } |
| |
| TEST(TracingTest, WritingToForbiddenFileFails) { |
| Tracing tracing; |
| |
| std::ofstream human_readable_file = OpenFile("/forbidden/unittest.ktrace"); |
| ASSERT_FALSE(human_readable_file); |
| |
| ASSERT_FALSE(tracing.WriteHumanReadable(human_readable_file)); |
| human_readable_file.close(); |
| } |
| |
| TEST(TracingTest, FetchRecordRetriesReadAndHandlesFailure) { |
| MockTracing mock_tracing; |
| |
| const size_t buf_len = 256; |
| uint8_t data_buf[buf_len]; |
| size_t bytes_read = 0; |
| uint32_t offset = 0; |
| |
| EXPECT_CALL(mock_tracing, ReadKernelBuffer) |
| .WillOnce(Invoke([](zx_handle_t, void*, uint32_t, size_t, size_t* bytes_read) { |
| *bytes_read = KTRACE_HDRSIZE - 5; |
| })) |
| .WillOnce( |
| Invoke([](zx_handle_t, void*, uint32_t, size_t, size_t* bytes_read) { *bytes_read = 0; })) |
| // Failure case. |
| .WillOnce(Invoke([](zx_handle_t, void*, uint32_t, size_t, size_t* bytes_read) { |
| *bytes_read = KTRACE_HDRSIZE - 5; |
| })) |
| // Read retry keeps track of total bytes read in current pass, so return 4 to keep total |
| // bytes_read less than KTRACE_HDRSIZE. |
| .WillOnce(Invoke( |
| [](zx_handle_t, void*, uint32_t, size_t, size_t* bytes_read) { *bytes_read = 4; })); |
| |
| auto [read_success, buf_end] = |
| mock_tracing.RealFetchRecord(0, data_buf, &offset, &bytes_read, buf_len); |
| ASSERT_TRUE(read_success); |
| |
| auto [read_fail, buf_end_fail] = |
| mock_tracing.RealFetchRecord(0, data_buf, &offset, &bytes_read, buf_len); |
| ASSERT_FALSE(read_fail); |
| } |
| |
| TEST(TracingTest, FetchRecordFailsWithZeroTagLength) { |
| MockTracing mock_tracing; |
| Tracing tracing; |
| |
| const size_t buf_len = 256; |
| uint8_t data_buf[buf_len]; |
| size_t bytes_read = 0; |
| uint32_t offset = 0; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG(0x25, KTRACE_GRP_ALL, 0)); |
| CreateMockKernelRead zero_tag_length(mock_tag, 0, 0); |
| |
| EXPECT_CALL(mock_tracing, ReadKernelBuffer).WillOnce(Invoke(zero_tag_length)); |
| |
| auto [read_success, buffer_end] = |
| mock_tracing.RealFetchRecord(0, data_buf, &offset, &bytes_read, buf_len); |
| ASSERT_FALSE(read_success); |
| } |
| |
| TEST(TracingTest, FetchRecordHandlesPayloads) { |
| MockTracing mock_tracing; |
| |
| const size_t buf_len = 256; |
| uint8_t data_buf[buf_len]; |
| size_t bytes_read = 0; |
| uint32_t offset = 0; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_32B(0x25, KTRACE_GRP_ALL)); |
| CreateMockKernelRead payload_present(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, ReadKernelBuffer) |
| .WillOnce(Invoke(payload_present)) |
| .WillOnce(Invoke( |
| [](zx_handle_t, void* data_buf, uint32_t offset, size_t len, size_t* bytes_read) -> void { |
| ktrace_header_t record = { |
| .tag = static_cast<uint32_t>(KTRACE_TAG_32B(0x25, KTRACE_GRP_ALL)), |
| .tid = 0, |
| .ts = 0, |
| }; |
| |
| if (len < KTRACE_HDRSIZE) |
| return; |
| |
| ktrace_header_t* record_ptr = reinterpret_cast<ktrace_header_t*>(data_buf); |
| *record_ptr = record; |
| record_ptr += offset; |
| |
| *bytes_read = sizeof(record); |
| })) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReadZeroBytes)); |
| |
| auto [read_success, buffer_end] = |
| mock_tracing.RealFetchRecord(0, data_buf, &offset, &bytes_read, buf_len); |
| ASSERT_TRUE(read_success); |
| } |
| |
| TEST(TracingTest, FetchRecordHandlesSmallDataBuffers) { |
| Tracing tracing; |
| |
| auto [read_success, buffer_end] = tracing.FetchRecord(0, nullptr, 0, nullptr, KTRACE_HDRSIZE - 1); |
| ASSERT_FALSE(read_success); |
| } |
| |
| TEST(TracingTest, ParseRecordHandlesLargeEvents) { |
| const size_t buf_len = 256; |
| uint8_t* data_buf; |
| |
| ktrace_header_t record = { |
| .tag = static_cast<uint32_t>(KTRACE_TAG_32B(static_cast<size_t>(-1), KTRACE_GRP_ALL)), |
| .tid = 0, |
| .ts = 0, |
| }; |
| |
| data_buf = reinterpret_cast<uint8_t*>(&record); |
| |
| auto ktrace_record_opt = KTraceRecord::ParseRecord(data_buf, buf_len); |
| ASSERT_TRUE(ktrace_record_opt); |
| ASSERT_TRUE(ktrace_record_opt.value().HasUnexpectedEvent()); |
| } |
| |
| TEST(TracingTest, ParseRecordHandlesSmallBuffers) { |
| KTraceRecord ktrace_record; |
| |
| ASSERT_FALSE(ktrace_record.ParseRecord(nullptr, KTRACE_HDRSIZE - 1)); |
| } |
| |
| TEST(TracingTest, RecordGettersDoNotReturnNullPointers) { |
| KTraceRecord ktrace_record; |
| |
| ktrace_header_t* rec_header; |
| ktrace_rec_32b_t* rec_32b; |
| ktrace_rec_name_t* rec_name; |
| |
| ASSERT_FALSE(ktrace_record.Get16BRecord(&rec_header)); |
| ASSERT_FALSE(ktrace_record.Get32BRecord(&rec_32b)); |
| ASSERT_FALSE(ktrace_record.GetNameRecord(&rec_name)); |
| ASSERT_FALSE(ktrace_record.Get64BitPayload()); |
| ASSERT_FALSE(ktrace_record.Get128BitPayload()); |
| ASSERT_FALSE(ktrace_record.GetFlowID()); |
| ASSERT_FALSE(ktrace_record.GetAssociatedThread()); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableStopsTraces) { |
| Tracing tracing; |
| |
| tracing.Start(KTRACE_GRP_ALL); |
| |
| std::ofstream human_readable_file = OpenFile("/tmp/unittest.ktrace"); |
| ASSERT_TRUE(human_readable_file); |
| |
| ASSERT_TRUE(tracing.WriteHumanReadable(human_readable_file)); |
| ASSERT_FALSE(tracing.running()); |
| human_readable_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableWritesCorrectFormat16B) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_16B(0x33, KTRACE_GRP_ALL)); |
| CreateMockTrace correct_format_16_B(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_16_B)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 k16BFormat{"^[0-9]+: [a-zA-Z_]+\\(0x[0-9a-f]+\\), arg 0x[0-9a-f]+$"}; |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, k16BFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableWritesCorrectFormat32B) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_32B(0x1, KTRACE_GRP_ALL)); |
| CreateMockTrace correct_format_32_B(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_32_B)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 k32BFormat{ |
| "^[0-9]+: [a-zA-Z_]+\\(0x[0-9a-e]+\\), tid 0x[0-9a-f]+, a 0x[0-9a-f]+, b 0x[0-9a-f]+, c " |
| "0x[0-9a-f]+, d 0x[0-9a-f]+$"}; |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, k32BFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableWritesCorrectFormatName) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_NAME(0x25, KTRACE_GRP_ALL)); |
| CreateMockTrace correct_format_name(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_name)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kNameFormat{"^[a-zA-Z_]+\\(0x[0-9a-f]+\\), id 0x[0-9a-f]+, arg 0x[0-9a-f]+, .*$"}; |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kNameFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableWritesCorrectFormatUnexpectedEvent) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_NAME(0xFFF, KTRACE_GRP_ALL)); |
| CreateMockTrace correct_format_unexpected_event(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_unexpected_event)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kUnexpectedFormat{"^Unexpected event: 0x[0-9a-f]+$"}; |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kUnexpectedFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesProbeRecord16) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_PROBE, 16, 1)); |
| CreateMockTrace correct_format_probe_record_16(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_probe_record_16)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kProbeRecord16Format{ |
| "^PROBE: tag 0x[0-9a-f]+, event_name_id 0x[0-9a-f]+, tid 0x[0-9a-f]+, ts [0-9]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kProbeRecord16Format)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesProbeRecord24) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_PROBE, 24, 1)); |
| CreateMockTrace correct_format_probe_record_24(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_probe_record_24)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kProbeRecord24Format{ |
| "^PROBE: tag 0x[0-9a-f]+, event_name_id 0x[0-9a-f]+, tid 0x[0-9a-f]+, ts [0-9]+, a " |
| "0x[0-9a-f]+, b 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kProbeRecord24Format)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesProbeRecord32) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_PROBE, 32, 1)); |
| CreateMockTrace correct_format_probe_record_32(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_probe_record_32)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kProbeRecord32Format{ |
| "^PROBE: tag 0x[0-9a-f]+, event_name_id 0x[0-9a-f]+, tid 0x[0-9a-f]+, ts [0-9]+, a " |
| "0x[0-9a-f]+, b 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kProbeRecord32Format)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesProbeRecordUnexpectedSize) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_PROBE, 0xFFF, 1)); |
| CreateMockTrace correct_format_probe_record_unexpected_size(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_probe_record_unexpected_size)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kProbeRecordUnexpectedFormat{"^Unexpected tag: 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kProbeRecordUnexpectedFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesDurationRecord16Begin) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = |
| static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 16, KTRACE_FLAGS_BEGIN)); |
| CreateMockTrace correct_format_duration_record_16_begin(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_duration_record_16_begin)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kDurationRecord16BeginFormat{ |
| "^[0-9]+: DURATION BEGIN: tag 0x[0-9a-f]+, id 0x[0-9a-f]+, tid 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kDurationRecord16BeginFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesDurationRecord16End) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = |
| static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 16, KTRACE_FLAGS_END)); |
| CreateMockTrace correct_format_duration_record_16_end(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_duration_record_16_end)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kDurationRecord16EndFormat{ |
| "^[0-9]+: DURATION END: tag 0x[0-9a-f]+, id 0x[0-9a-f]+, tid 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kDurationRecord16EndFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesDurationRecord16Unexpected) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 16, 1)); |
| CreateMockTrace correct_format_duration_record_16_unexpected(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_duration_record_16_unexpected)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kDurationRecord16UnexpectedFormat{"^Unexpected tag: 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kDurationRecord16UnexpectedFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesDurationRecord32Begin) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = |
| static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 32, KTRACE_FLAGS_BEGIN)); |
| CreateMockTrace correct_format_duration_record_32_begin(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_duration_record_32_begin)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kDurationRecord32BeginFormat{ |
| "^[0-9]+: DURATION BEGIN: tag 0x[0-9a-f]+, id 0x[0-9a-f]+, tid 0x[0-9a-f]+, a 0x[0-9a-f]+, b " |
| "0x" |
| "[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kDurationRecord32BeginFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesDurationRecord32End) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = |
| static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 32, KTRACE_FLAGS_END)); |
| CreateMockTrace correct_format_duration_record_32_end(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_duration_record_32_end)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kDurationRecord32EndFormat{ |
| "^[0-9]+: DURATION END: tag 0x[0-9a-f]+, id 0x[0-9a-f]+, tid 0x[0-9a-f]+, a 0x[0-9a-f]+, b 0x" |
| "[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kDurationRecord32EndFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesDurationRecord32Unexpected) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 32, 1)); |
| CreateMockTrace correct_format_duration_record_32_unexpected(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_duration_record_32_unexpected)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kDurationRecord32UnexpectedFormat{"^Unexpected tag: 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kDurationRecord32UnexpectedFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesDurationRecord32UnexpectedSize) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = |
| static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 0xFFF, KTRACE_FLAGS_BEGIN)); |
| CreateMockTrace correct_format_duration_record_32_unexpected_size(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_duration_record_32_unexpected_size)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kDurationRecordUnexpectedFormat{"^Unexpected tag: 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kDurationRecordUnexpectedFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesFlowRecord32Begin) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>( |
| KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 32, KTRACE_FLAGS_FLOW | KTRACE_FLAGS_BEGIN)); |
| CreateMockTrace correct_format_flow_record_32_begin(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_flow_record_32_begin)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kFlowRecord32BeginFormat{ |
| "^[0-9]+: FLOW BEGIN: tag 0x[0-9a-f]+, id 0x[0-9a-f]+, tid 0x[0-9a-f]+, flow id " |
| "0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kFlowRecord32BeginFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesFlowRecord32End) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>( |
| KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 32, KTRACE_FLAGS_FLOW | KTRACE_FLAGS_END)); |
| CreateMockTrace correct_format_flow_record_32_end(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_flow_record_32_end)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kFlowRecord32EndFormat{ |
| "^[0-9]+: FLOW END: tag 0x[0-9a-f]+, id 0x[0-9a-f]+, tid 0x[0-9a-f]+, flow id " |
| "0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kFlowRecord32EndFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesFlowRecord32Unexpected) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = |
| static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 32, KTRACE_FLAGS_FLOW)); |
| CreateMockTrace correct_format_flow_record_32_unexpected(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_flow_record_32_unexpected)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kFlowRecord32UnexpectedFormat{"^Unexpected tag: 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kFlowRecord32UnexpectedFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, WriteHumanReadableHandlesFlowRecordUnexpectedSize) { |
| MockTracing mock_tracing; |
| |
| uint32_t mock_tag = static_cast<uint32_t>( |
| KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 0xFFF, KTRACE_FLAGS_FLOW | KTRACE_FLAGS_BEGIN)); |
| CreateMockTrace correct_format_flow_record_unexpected_size(mock_tag, 0, 0); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(correct_format_flow_record_unexpected_size)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| std::string filepath = "/tmp/unittest.ktrace"; |
| std::ofstream human_readable_file = OpenFile(filepath); |
| ASSERT_TRUE(human_readable_file); |
| |
| mock_tracing.WriteHumanReadable(human_readable_file); |
| human_readable_file.close(); |
| |
| std::ifstream in_file; |
| in_file.open(filepath); |
| ASSERT_TRUE(in_file); |
| |
| std::string first_line; |
| getline(in_file, first_line); |
| |
| const re2::RE2 kFlowRecordUnexpectedFormat{"^Unexpected tag: 0x[0-9a-f]+$"}; |
| |
| ASSERT_TRUE(re2::RE2::PartialMatch(first_line, kFlowRecordUnexpectedFormat)); |
| |
| in_file.close(); |
| } |
| |
| TEST(TracingTest, DurationStatsStopsTraces) { |
| Tracing tracing; |
| |
| tracing.Start(KTRACE_GRP_ALL); |
| |
| std::vector<Tracing::DurationStats> duration_stats; |
| std::map<uint64_t, Tracing::QueuingStats> queuing_stats; |
| |
| tracing.PopulateDurationStats("", &duration_stats, &queuing_stats); |
| ASSERT_FALSE(tracing.running()); |
| } |
| |
| TEST(TracingTest, DurationStatsFindsStringRef) { |
| MockTracing mock_tracing; |
| |
| std::vector<MockTracing::DurationStats> duration_stats; |
| std::map<uint64_t, MockTracing::QueuingStats> queuing_stats; |
| |
| uint32_t mock_tag = static_cast<uint32_t>(KTRACE_TAG_NAME(0x25, KTRACE_GRP_ALL)); |
| const char* expected_string_ref = "expected_string_ref"; |
| CreateMockNameTrace string_ref_to_find(mock_tag, 0, 0, 0, expected_string_ref); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(string_ref_to_find)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| ASSERT_TRUE( |
| mock_tracing.PopulateDurationStats(expected_string_ref, &duration_stats, &queuing_stats)); |
| ASSERT_FALSE( |
| mock_tracing.PopulateDurationStats("unexpected_string_ref", &duration_stats, &queuing_stats)); |
| } |
| |
| TEST(TracingTest, DurationStatsHandlesEmptyPayloads) { |
| MockTracing mock_tracing; |
| |
| std::vector<MockTracing::DurationStats> duration_stats; |
| std::map<uint64_t, MockTracing::QueuingStats> queuing_stats; |
| |
| uint32_t mock_name_tag = static_cast<uint32_t>(KTRACE_TAG_NAME(0x25, KTRACE_GRP_ALL)); |
| uint32_t mock_name_id = 0x14; |
| uint32_t mock_begin_tag = |
| // static_cast<uint32_t>(KTRACE_TAG_EX(0x25, KTRACE_GRP_SCHEDULER, 16, KTRACE_FLAGS_BEGIN)); |
| static_cast<uint32_t>(TAG_BEGIN_DURATION_16(mock_name_id, KTRACE_GRP_SCHEDULER)); |
| uint32_t mock_end_tag = |
| static_cast<uint32_t>(TAG_END_DURATION_16(mock_name_id, KTRACE_GRP_SCHEDULER)); |
| const char* expected_string_ref = "expected_string_ref"; |
| const uint64_t begin_ts = 12345678; |
| const uint64_t end_ts = begin_ts - 12345; |
| |
| CreateMockNameTrace string_ref_to_find(mock_name_tag, 0, 0, mock_name_id, expected_string_ref); |
| CreateMockTrace begin_duration_record(mock_begin_tag, 0, begin_ts); |
| CreateMockTrace end_duration_record(mock_end_tag, 0, end_ts); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(string_ref_to_find)) |
| .WillOnce(Invoke(begin_duration_record)) |
| .WillOnce(Invoke(end_duration_record)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| ASSERT_TRUE( |
| mock_tracing.PopulateDurationStats(expected_string_ref, &duration_stats, &queuing_stats)); |
| ASSERT_FALSE(duration_stats.empty()); |
| ASSERT_FALSE(duration_stats.front().payload); |
| ASSERT_EQ(duration_stats.front().begin_ts_ns, begin_ts); |
| ASSERT_EQ(duration_stats.front().end_ts_ns, end_ts); |
| ASSERT_EQ(duration_stats.front().wall_duration_ns, end_ts - begin_ts); |
| } |
| |
| TEST(TracingTest, DurationStatsHandlesPayloads) { |
| MockTracing mock_tracing; |
| |
| std::vector<MockTracing::DurationStats> duration_stats; |
| std::map<uint64_t, MockTracing::QueuingStats> queuing_stats; |
| |
| uint32_t mock_name_tag = static_cast<uint32_t>(KTRACE_TAG_NAME(0x25, KTRACE_GRP_ALL)); |
| uint32_t mock_name_id = 0x14; |
| uint32_t mock_begin_tag = |
| static_cast<uint32_t>(TAG_BEGIN_DURATION_32(mock_name_id, KTRACE_GRP_SCHEDULER)); |
| uint32_t mock_end_tag = |
| static_cast<uint32_t>(TAG_END_DURATION_32(mock_name_id, KTRACE_GRP_SCHEDULER)); |
| const char* expected_string_ref = "expected_string_ref"; |
| const uint64_t begin_ts = 12345678; |
| const uint64_t end_ts = begin_ts - 12345; |
| const uint64_t a = 9876; |
| const uint64_t b = 54321; |
| |
| CreateMockNameTrace string_ref_to_find(mock_name_tag, 0, 0, mock_name_id, expected_string_ref); |
| CreateMockTrace128BitPayload begin_duration_record(mock_begin_tag, 0, begin_ts, a, b); |
| CreateMockTrace128BitPayload end_duration_record(mock_end_tag, 0, end_ts, a, b); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(string_ref_to_find)) |
| .WillOnce(Invoke(begin_duration_record)) |
| .WillOnce(Invoke(end_duration_record)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| ASSERT_TRUE( |
| mock_tracing.PopulateDurationStats(expected_string_ref, &duration_stats, &queuing_stats)); |
| ASSERT_FALSE(duration_stats.empty()); |
| ASSERT_TRUE(duration_stats.front().payload); |
| ASSERT_EQ(duration_stats.front().begin_ts_ns, begin_ts); |
| ASSERT_EQ(duration_stats.front().end_ts_ns, end_ts); |
| ASSERT_EQ(duration_stats.front().wall_duration_ns, end_ts - begin_ts); |
| ASSERT_EQ(duration_stats.front().payload.value()[0], a); |
| ASSERT_EQ(duration_stats.front().payload.value()[1], b); |
| } |
| |
| TEST(TracingTest, DurationStatsHandlesFlowRecords) { |
| MockTracing mock_tracing; |
| |
| std::vector<MockTracing::DurationStats> duration_stats; |
| std::map<uint64_t, MockTracing::QueuingStats> queuing_stats; |
| |
| uint32_t mock_name_tag = static_cast<uint32_t>(KTRACE_TAG_NAME(0x25, KTRACE_GRP_ALL)); |
| uint32_t mock_name_id = 0x14; |
| uint32_t mock_begin_tag = |
| static_cast<uint32_t>(TAG_FLOW_BEGIN(mock_name_id, KTRACE_GRP_SCHEDULER)); |
| uint32_t mock_end_tag = static_cast<uint32_t>(TAG_FLOW_END(mock_name_id, KTRACE_GRP_SCHEDULER)); |
| const char* expected_string_ref = "expected_string_ref"; |
| const uint64_t begin_ts = 12345678; |
| const uint64_t end_ts = begin_ts - 12345; |
| const uint64_t flow_id = 9876; |
| const uint64_t associated_thread = 54321; |
| |
| CreateMockNameTrace string_ref_to_find(mock_name_tag, 0, 0, mock_name_id, expected_string_ref); |
| CreateMockTrace128BitPayload begin_duration_record(mock_begin_tag, 0, begin_ts, flow_id, |
| associated_thread); |
| CreateMockTrace128BitPayload end_duration_record(mock_end_tag, 0, end_ts, flow_id, |
| associated_thread); |
| ReaderHelper reader_helper; |
| |
| EXPECT_CALL(mock_tracing, FetchRecord) |
| .WillOnce(Invoke(string_ref_to_find)) |
| .WillOnce(Invoke(begin_duration_record)) |
| .WillOnce(Invoke(end_duration_record)) |
| .WillRepeatedly(Invoke(&reader_helper, &ReaderHelper::ReturnEndOfKernelBuffer)); |
| |
| ASSERT_TRUE( |
| mock_tracing.PopulateDurationStats(expected_string_ref, &duration_stats, &queuing_stats)); |
| ASSERT_FALSE(queuing_stats.empty()); |
| ASSERT_EQ(queuing_stats.begin()->first, flow_id); |
| ASSERT_EQ(queuing_stats.begin()->second.begin_ts_ns, begin_ts); |
| ASSERT_EQ(queuing_stats.begin()->second.end_ts_ns, end_ts); |
| ASSERT_EQ(queuing_stats.begin()->second.queuing_time_ns, end_ts - begin_ts); |
| ASSERT_EQ(queuing_stats.begin()->second.associated_thread, associated_thread); |
| } |
| } // anonymous namespace |