xref: /aosp_15_r20/external/federated-compute/fcp/tracing/test_tracing_recorder.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
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