xref: /aosp_15_r20/external/federated-compute/fcp/tracing/tracing_span.h (revision 14675a029014e728ec732f129a32e299b2da0601)
1 // Copyright 2020 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 #ifndef FCP_TRACING_TRACING_SPAN_H_
16 #define FCP_TRACING_TRACING_SPAN_H_
17 
18 #include <memory>
19 #include <string>
20 #include <utility>
21 
22 #include "fcp/base/error.h"
23 #include "fcp/tracing/tracing_recorder_impl.h"
24 #include "fcp/tracing/tracing_span_impl.h"
25 #include "fcp/tracing/tracing_span_ref.h"
26 #include "fcp/tracing/tracing_traits.h"
27 #include "flatbuffers/flatbuffers.h"
28 
29 namespace fcp {
30 
31 namespace tracing_internal {
32 
33 template <class FlatBufferTable>
AssertIsNotSpan()34 void AssertIsNotSpan() {
35   static_assert(TracingTraits<FlatBufferTable>::kIsSpan == false,
36                 "Trace can only be called on an event table, not a span. To "
37                 "convert the table to an event remove 'span' from the table "
38                 "attributes.");
39 }
40 
41 template <class FlatBufferTable>
AssertIsError()42 void AssertIsError() {
43   static_assert(
44       TracingTraits<FlatBufferTable>::kSeverity == TracingSeverity::kError,
45       "TraceError can only be called on table of severity Error. To "
46       "convert the table to error severity add 'error' to the "
47       "table attributes.");
48 }
49 
50 // Generic method to build FlatBuff tables from a set of args.
51 template <class FlatBufferTable, class... Arg>
BuildFlatBuffer(Arg &&...args)52 flatbuffers::DetachedBuffer BuildFlatBuffer(Arg&&... args) {
53   flatbuffers::FlatBufferBuilder fbb;
54   flatbuffers::Offset<FlatBufferTable> root =
55       TracingTraits<FlatBufferTable>::Create(std::forward<Arg>(args)..., &fbb);
56   constexpr TracingTag tag = TracingTraits<FlatBufferTable>::kTag;
57   constexpr char signature[5] = {tag.data[0], tag.data[1], tag.data[2],
58                                  tag.data[3], 0};
59   fbb.Finish(root, signature);
60   return fbb.Release();
61 }
62 
63 /*
64  * Un-templatized version of the tracing span which stores the raw
65  * definition of the tracing span and returns a reference to it.
66  *
67  * This class should not/cannot be publicly initialized, instead it should be
68  * used as a base class for both UnscopedTracingSpan and TracingSpan.
69  *
70  * For Java based tracing spans, all native spans will be cast to this common
71  * structure to pass along the JNI boundary.
72  */
73 class TracingSpanBase {
74  public:
75   // Returns a reference to this span.
Ref()76   inline TracingSpanRef Ref() const { return impl_->Ref(); }
77   virtual ~TracingSpanBase() = default;
78 
79  protected:
80   explicit TracingSpanBase() = default;
81   // Set by the child classes after an instance of TracingSpanImpl is created
82   // from the flatbuf definitions.
83   std::unique_ptr<TracingSpanImpl> impl_;
84 };
85 
86 }  // namespace tracing_internal
87 
88 // Unscoped tracing span carries tracing context for some logical activity.
89 // The primary purpose of this class is to keep the tracing context of a
90 // long-running asynchronous activity, typically associated with the lifetime of
91 // some long-lived object.
92 //
93 // This span is not scoped to a block/function and need not necessarily be
94 // instantiated as a local variable on the stack. It is OK to initialise this
95 // span on a heap and/or as a field member of a class. For scoped variant,
96 // prefer using TracingSpan instead.
97 //
98 // To record a Trace within an UnscopedTracingSpan, the caller must explicitly
99 // pass a reference to a span e.g Trace<FlatBuff>(span, args...).
100 //
101 // The class is NOT thread-safe, but OK to use from different threads if invoked
102 // sequentially (i.e. with external synchronisation providing sequential
103 // consistency memory ordering).
104 //
105 // Recommended usage is to create new child span for every sub-activity or
106 // operation.
107 template <class FlatBufferTable>
108 class UnscopedTracingSpan : public tracing_internal::TracingSpanBase {
109  public:
110   // UnscopedTracingSpan is neither copyable nor movable.
111   UnscopedTracingSpan(const UnscopedTracingSpan&) = delete;
112   UnscopedTracingSpan& operator=(const UnscopedTracingSpan&) = delete;
113   // Public constructors allow creating new tracing span.
114   // A parent span reference can be optionally provided as a first argument.
115   // By default current span is used as a parent.
116   template <class... Arg>
117   explicit UnscopedTracingSpan(Arg&&... args);
118   template <class... Arg>
119   explicit UnscopedTracingSpan(TracingSpanRef parent, Arg&&... args);
120 
121  private:
122   template <class... Arg>
123   void Create(TracingSpanRef parent, Arg&&... args);
124 };
125 
126 template <class FlatBufferTable>
127 template <class... Arg>
UnscopedTracingSpan(Arg &&...args)128 UnscopedTracingSpan<FlatBufferTable>::UnscopedTracingSpan(Arg&&... args) {
129   Create(TracingSpanRef::Top(), std::forward<Arg>(args)...);
130 }
131 
132 template <class FlatBufferTable>
133 template <class... Arg>
UnscopedTracingSpan(TracingSpanRef parent,Arg &&...args)134 UnscopedTracingSpan<FlatBufferTable>::UnscopedTracingSpan(TracingSpanRef parent,
135                                                           Arg&&... args) {
136   Create(parent, std::forward<Arg>(args)...);
137 }
138 
139 template <class FlatBufferTable>
140 template <class... Arg>
Create(TracingSpanRef parent,Arg &&...args)141 void UnscopedTracingSpan<FlatBufferTable>::Create(TracingSpanRef parent,
142                                                   Arg&&... args) {
143   static_assert(
144       TracingTraits<FlatBufferTable>::kIsSpan == true,
145       "UnscopedTracingSpan can only be created from a span table, not "
146       "an event. To convert the table to an span add 'span' "
147       "to the table attributes.");
148   flatbuffers::DetachedBuffer trace_data =
149       tracing_internal::BuildFlatBuffer<FlatBufferTable>(
150           std::forward<Arg>(args)...);
151   impl_ = parent.recorder()->CreateChildSpan(parent.span_id(),
152                                              std::move(trace_data),
153                                              TracingTraits<FlatBufferTable>());
154 }
155 
156 // Tracing span, carrying abstract tracing context for some logical activity
157 // performed by the application. Provides ability to log various events
158 // which are automatically associated with such a context, thus allowing logging
159 // essential information only.
160 //
161 // This class uses RAII-style mechanism of entering/existing the tracing span
162 // for the duration of a scoped block or a function, this class must be
163 // instantiated as a local variable on the stack, in a similar manner as
164 // std::lock_guard.
165 //
166 // The class is NOT thread-safe, but OK to use from different threads if invoked
167 // sequentially (i.e. with external synchronisation providing sequential
168 // consistency memory ordering).
169 //
170 // Recommended usage is to create new child span for every sub-activity or
171 // operation.
172 //
173 // For a more general variant that is not necessarily tied to a scoped block,
174 // prefer using UnscopedTracingSpan directly.
175 template <class FlatBufferTable>
176 class TracingSpan final : public UnscopedTracingSpan<FlatBufferTable> {
177  public:
178   // Since this manipulates TLS/FLS in RAII fashion, this is intended to be
179   // used as a stack local (no heap alloc allowed):
180   void* operator new(std::size_t) = delete;
181   void* operator new[](std::size_t) = delete;
182   // Public constructors allow creating new tracing span.
183   template <class... Arg>
TracingSpan(Arg &&...args)184   explicit TracingSpan(Arg&&... args)
185       : UnscopedTracingSpan<FlatBufferTable>(std::forward<Arg>(args)...) {
186     UnscopedTracingSpan<FlatBufferTable>::impl_->Push();
187   }
188   template <class... Arg>
TracingSpan(TracingSpanRef parent,Arg &&...args)189   explicit TracingSpan(TracingSpanRef parent, Arg&&... args)
190       : UnscopedTracingSpan<FlatBufferTable>(parent,
191                                              std::forward<Arg>(args)...) {
192     UnscopedTracingSpan<FlatBufferTable>::impl_->Push();
193   }
194 
195   // Destructor closes the span
~TracingSpan()196   ~TracingSpan() override {
197     UnscopedTracingSpan<FlatBufferTable>::impl_->Pop();
198   }
199 };
200 
201 // Writes a trace with the specified args under the topmost span located on
202 // TLS. If no span exists on TLS, then root span is fetched from the global
203 // recorder.
204 template <class FlatBufferTable, class... Arg>
Trace(Arg &&...args)205 void Trace(Arg&&... args) {
206   tracing_internal::AssertIsNotSpan<FlatBufferTable>();
207   flatbuffers::DetachedBuffer trace_data =
208       tracing_internal::BuildFlatBuffer<FlatBufferTable>(
209           std::forward<Arg>(args)...);
210   // Now discover what tracing span to log that with:
211   tracing_internal::TracingSpanImpl* top =
212       tracing_internal::TracingSpanImpl::Top();
213   if (top != nullptr) {
214     // Fast path: getting top tracing span from TLS/FCB:
215     top->TraceImpl(std::move(trace_data), TracingTraits<FlatBufferTable>());
216   } else {
217     // Slow path, finding root span from global recorder. This
218     // involves increasing its reference counter:
219     std::shared_ptr<tracing_internal::TracingRecorderImpl> recorder =
220         tracing_internal::TracingRecorderImpl::GetCurrent();
221     recorder->GetRootSpan()->TraceImpl(std::move(trace_data),
222                                        TracingTraits<FlatBufferTable>());
223     // now, since we done with using root span, it is safe to release shared_ptr
224   }
225 }
226 
227 // Writes a trace under the specified span with the given args.
228 template <class FlatBufferTable, class... Arg>
Trace(TracingSpanRef span,Arg &&...args)229 void Trace(TracingSpanRef span, Arg&&... args) {
230   tracing_internal::AssertIsNotSpan<FlatBufferTable>();
231   flatbuffers::DetachedBuffer trace_data =
232       tracing_internal::BuildFlatBuffer<FlatBufferTable>(
233           std::forward<Arg>(args)...);
234   span.recorder()->TraceImpl(span.span_id(), std::move(trace_data),
235                              TracingTraits<FlatBufferTable>());
236 }
237 
238 // Writes an error trace with the specified args under the topmost span located
239 // on TLS. If no span exists on TLS, then root span is fetched from the global
240 // recorder.
241 template <class FlatBufferTable, class... Arg>
TraceError(Arg &&...args)242 ABSL_MUST_USE_RESULT Error TraceError(Arg&&... args) {
243   tracing_internal::AssertIsError<FlatBufferTable>();
244   Trace<FlatBufferTable>(args...);
245   return Error(Error::ConstructorAccess{});
246 }
247 
248 }  // namespace fcp
249 
250 #endif  // FCP_TRACING_TRACING_SPAN_H_
251