1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "fcp/tracing/test_tracing_recorder.h"
16
17 #include <algorithm>
18 #include <memory>
19 #include <string>
20 #include <utility>
21
22 #include "fcp/tracing/test_tracing_recorder_impl.h"
23 #include "fcp/tracing/tracing_severity.h"
24 #include "fcp/tracing/tracing_tag.h"
25 #include "fcp/tracing/tracing_traits.h"
26
27 namespace fcp {
28
TestTracingRecorder(SourceLocation loc)29 TestTracingRecorder::TestTracingRecorder(SourceLocation loc)
30 : loc_(loc),
31 impl_(std::shared_ptr<tracing_internal::TestTracingRecorderImpl>(
32 new tracing_internal::TestTracingRecorderImpl(this))) {
33 InstallAsGlobal();
34 }
35
~TestTracingRecorder()36 TestTracingRecorder::~TestTracingRecorder() {
37 UninstallAsGlobal();
38 std::vector<std::string> unseen_error_names;
39 for (TracingTag tag : unseen_expected_errors_) {
40 unseen_error_names.push_back(TracingTraitsBase::Lookup(tag)->Name());
41 }
42 EXPECT_THAT(unseen_error_names, testing::IsEmpty())
43 << "Errors marked as expected with TestTracingRecorder::ExpectEvent<E>() "
44 << "weren't traced via TestTracingRecorder with source location: "
45 << std::endl
46 << loc_.file_name() << ":" << loc_.line() << std::endl;
47 }
48
TraitsForRecord(TestTracingRecorder::Record * record)49 TracingTraitsBase const* TraitsForRecord(TestTracingRecorder::Record* record) {
50 return TracingTraitsBase::Lookup(*TracingTag::FromFlatbuf(record->data));
51 }
52
Format(TracingTraitsBase const * traits,TestTracingRecorder::Record * record)53 std::string Format(TracingTraitsBase const* traits,
54 TestTracingRecorder::Record* record) {
55 return absl::StrCat(traits->Name(), " ", traits->TextFormat(record->data));
56 }
57
OnRoot(TracingSpanId id,flatbuffers::DetachedBuffer data)58 void TestTracingRecorder::OnRoot(TracingSpanId id,
59 flatbuffers::DetachedBuffer data) {
60 auto unique_record = std::make_unique<Record>(id, std::move(data));
61 root_record_ = unique_record.get();
62 {
63 absl::MutexLock locked(&map_lock_);
64 FCP_CHECK(id_to_record_map_.empty());
65 id_to_record_map_[id] = std::move(unique_record);
66 }
67 }
68
OnTrace(TracingSpanId parent_id,TracingSpanId id,flatbuffers::DetachedBuffer data)69 void TestTracingRecorder::OnTrace(TracingSpanId parent_id, TracingSpanId id,
70 flatbuffers::DetachedBuffer data) {
71 auto unique_record = std::make_unique<Record>(parent_id, id, std::move(data));
72 // Entries in id_to_record_map_ will never be removed or modified. Due to this
73 // invariant we can be sure that a pointer to the record will not be
74 // invalidated by a concurrent modification of the map causing destruction of
75 // the record.
76 Record* record = unique_record.get();
77 {
78 absl::MutexLock locked(&map_lock_);
79 id_to_record_map_[id] = std::move(unique_record);
80 }
81 TracingTraitsBase const* traits = TraitsForRecord(record);
82 if (traits->Severity() == TracingSeverity::kError) {
83 absl::MutexLock locked(&expected_errors_lock_);
84 // We're interested here in errors only:
85 TracingTag error_tag = *TracingTag::FromFlatbuf(record->data);
86 EXPECT_TRUE(expected_errors_.contains(error_tag))
87 << "Unexpected error " << Format(traits, record)
88 << " is traced via TestTracingRecorder with source location: "
89 << std::endl
90 << loc_.file_name() << ":" << loc_.line() << std::endl
91 << "Use TracingRecorder::ExpectError<" << traits->Name()
92 << ">() in the beginning of the unit "
93 << "test to allowlist this error as expected" << std::endl;
94 unseen_expected_errors_.erase(error_tag);
95 }
96 }
97
SpanOrEvent(TestTracingRecorder * recorder,Record * record)98 TestTracingRecorder::SpanOrEvent::SpanOrEvent(TestTracingRecorder* recorder,
99 Record* record)
100 : record_(record), recorder_(recorder) {
101 std::vector<Record*> children = recorder_->GetChildren(record_);
102
103 children_.reserve(children.size());
104 for (Record* c : children) {
105 children_.emplace_back(recorder_, c);
106 }
107 }
108
TextFormat() const109 std::string TestTracingRecorder::SpanOrEvent::TextFormat() const {
110 return Format(traits(), record_);
111 }
112
traits() const113 TracingTraitsBase const* TestTracingRecorder::SpanOrEvent::traits() const {
114 return TraitsForRecord(record_);
115 }
116
InstallAsGlobal()117 void TestTracingRecorder::InstallAsGlobal() { impl_->InstallAsGlobal(); }
118
UninstallAsGlobal()119 void TestTracingRecorder::UninstallAsGlobal() { impl_->UninstallAsGlobal(); }
120
InstallAsThreadLocal()121 void TestTracingRecorder::InstallAsThreadLocal() {
122 impl_->InstallAsThreadLocal();
123 }
124
UninstallAsThreadLocal()125 void TestTracingRecorder::UninstallAsThreadLocal() {
126 impl_->UninstallAsThreadLocal();
127 }
128
129 struct RecordComparison {
operator ()fcp::RecordComparison130 bool const operator()(TestTracingRecorder::Record* r1,
131 TestTracingRecorder::Record* r2) const {
132 return r1->id < r2->id;
133 }
134 };
135
GetChildren(Record * parent)136 std::vector<TestTracingRecorder::Record*> TestTracingRecorder::GetChildren(
137 Record* parent) {
138 std::vector<Record*> children;
139 {
140 absl::MutexLock locked(&map_lock_);
141 // Search through the entire hashmap for children of this parent.
142 // Note that this is O(n) in terms of the total number of traces, rather
143 // than in terms of the number of children.
144 for (const auto& [id, record_unique_ptr] : id_to_record_map_) {
145 Record* record = record_unique_ptr.get();
146 if (record->parent_id.has_value() &&
147 record->parent_id.value() == parent->id) {
148 children.push_back(record);
149 }
150 }
151 }
152 // Sort in order of lowest to highest ID, which should be in order of creation
153 // time.
154 std::sort(children.begin(), children.end(), RecordComparison());
155 return children;
156 }
157
root()158 TestTracingRecorder::RootSpan TestTracingRecorder::root() {
159 return RootSpan(this, root_record_);
160 }
161
162 } // namespace fcp
163