• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "perfetto.h"
2 #include "perfetto-tracing-only.h"
3 #include "perfetto_trace.pb.h"
4 
5 #include <string>
6 #include <thread>
7 #include <fstream>
8 
9 PERFETTO_DEFINE_CATEGORIES(
10     ::perfetto::Category("gfx")
11         .SetDescription("Events from the graphics subsystem"));
12 PERFETTO_TRACK_EVENT_STATIC_STORAGE();
13 
14 #define TRACE_COUNTER(category, name, value)  \
15     PERFETTO_INTERNAL_TRACK_EVENT(                  \
16             category, name, \
17             ::perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER, [&](::perfetto::EventContext ctx){ \
18             ctx.event()->set_counter_value(value); \
19             })
20 
21 #ifdef __cplusplus
22 #   define CC_LIKELY( exp )    (__builtin_expect( !!(exp), true ))
23 #   define CC_UNLIKELY( exp )  (__builtin_expect( !!(exp), false ))
24 #else
25 #   define CC_LIKELY( exp )    (__builtin_expect( !!(exp), 1 ))
26 #   define CC_UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))
27 #endif
28 
29 namespace virtualdeviceperfetto {
30 
31 static bool sPerfettoInitialized = false;
32 
33 static VirtualDeviceTraceConfig sTraceConfig = {
34     .initialized = false,
35     .tracingDisabled = true,
36     .packetsWritten = 0,
37     .sequenceIdWritten = 0,
38     .currentInterningId = 1,
39     .currentThreadId = 1,
40     .hostFilename = "vmm.trace",
41     .guestFilename = nullptr,
42     .combinedFilename = nullptr,
43     .hostStartTime = 0,
44     .guestStartTime = 0,
45     .guestTimeDiff = 0,
46     .perThreadStorageMb = 1,
47 };
48 
setTraceConfig(std::function<void (VirtualDeviceTraceConfig &)> f)49 PERFETTO_TRACING_ONLY_EXPORT void setTraceConfig(std::function<void(VirtualDeviceTraceConfig&)> f) {
50     f(sTraceConfig);
51 }
52 
queryTraceConfig()53 PERFETTO_TRACING_ONLY_EXPORT VirtualDeviceTraceConfig queryTraceConfig() {
54     return sTraceConfig;
55 }
56 
initialize(const bool ** tracingDisabledPtr)57 PERFETTO_TRACING_ONLY_EXPORT void initialize(const bool** tracingDisabledPtr) {
58     if (!sPerfettoInitialized) {
59         ::perfetto::TracingInitArgs args;
60         args.backends |= ::perfetto::kInProcessBackend;
61         ::perfetto::Tracing::Initialize(args);
62         ::perfetto::TrackEvent::Register();
63         sPerfettoInitialized = true;
64     }
65 
66     // An optimization to have faster queries of whether tracing is enabled.
67     *tracingDisabledPtr = &sTraceConfig.tracingDisabled;
68 }
69 
70 static std::unique_ptr<::perfetto::TracingSession> sTracingSession;
71 
useFilenameByEnv(const char * s)72 bool useFilenameByEnv(const char* s) {
73     return s && ("" != std::string(s));
74 }
75 
enableTracing()76 PERFETTO_TRACING_ONLY_EXPORT void enableTracing() {
77     const char* hostFilenameByEnv = std::getenv("VPERFETTO_HOST_FILE");
78     const char* guestFilenameByEnv = std::getenv("VPERFETTO_GUEST_FILE");
79     const char* combinedFilenameByEnv = std::getenv("VPERFETTO_COMBINED_FILE");
80 
81     if (useFilenameByEnv(hostFilenameByEnv)) {
82         sTraceConfig.hostFilename = hostFilenameByEnv;
83     }
84 
85     if (useFilenameByEnv(guestFilenameByEnv)) {
86         sTraceConfig.guestFilename = guestFilenameByEnv;
87     }
88 
89     if (useFilenameByEnv(combinedFilenameByEnv)) {
90         sTraceConfig.combinedFilename = combinedFilenameByEnv;
91     }
92 
93     // Don't enable tracing if host filename is null
94     if (!sTraceConfig.hostFilename) return;
95 
96     // Don't enable it twice
97     if (!sTraceConfig.tracingDisabled) return;
98 
99     if (!sTracingSession) {
100         fprintf(stderr, "%s: Tracing begins================================================================================\n", __func__);
101         fprintf(stderr, "%s: Configuration:\n", __func__);
102         fprintf(stderr, "%s: host filename: %s (possibly set via $VPERFETTO_HOST_FILE)\n", __func__, sTraceConfig.hostFilename);
103         fprintf(stderr, "%s: guest filename: %s (possibly set via $VPERFETTO_GUEST_FILE)\n", __func__, sTraceConfig.guestFilename);
104         fprintf(stderr, "%s: combined filename: %s (possibly set via $VPERFETTO_COMBINED_FILE)\n", __func__, sTraceConfig.combinedFilename);
105         fprintf(stderr, "%s: guest time diff to add to host time: %llu\n", __func__, (unsigned long long)sTraceConfig.guestTimeDiff);
106 
107         auto desc = ::perfetto::ProcessTrack::Current().Serialize();
108         desc.mutable_process()->set_process_name("VirtualMachineMonitorProcess");
109         ::perfetto::TrackEvent::SetTrackDescriptor(::perfetto::ProcessTrack::Current(), desc);
110 
111         ::perfetto::TraceConfig cfg;
112         ::perfetto::protos::gen::TrackEventConfig track_event_cfg;
113         cfg.add_buffers()->set_size_kb(1024 * 100);  // Record up to 100 MiB.
114         auto* ds_cfg = cfg.add_data_sources()->mutable_config();
115         ds_cfg->set_name("track_event");
116         ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
117 
118         // Disable service events in the host trace, because they interfere
119         // with the guest's and we end up dropping packets on one side or the other.
120         auto* builtin_ds_cfg = cfg.mutable_builtin_data_sources();
121         builtin_ds_cfg->set_disable_service_events(true);
122 
123         sTracingSession = ::perfetto::Tracing::NewTrace();
124         sTracingSession->Setup(cfg);
125         sTracingSession->StartBlocking();
126         sTraceConfig.tracingDisabled = false;
127     }
128 }
129 
asyncTraceSaveFunc()130 void asyncTraceSaveFunc() {
131     fprintf(stderr, "%s: Saving combined trace async...\n", __func__);
132 
133     static const int kWaitSecondsPerIteration = 1;
134     static const int kMaxIters = 20;
135     static const int kMinItersForGuestFileSize = 2;
136 
137     const char* hostFilename = sTraceConfig.hostFilename;
138     const char* guestFilename = sTraceConfig.guestFilename;
139     const char* combinedFilename = sTraceConfig.combinedFilename;
140 
141     std::streampos currGuestSize = 0;
142     int numGoodGuestFileSizeIters = 0;
143     bool good = false;
144 
145     for (int i = 0; i < kMaxIters; ++i) {
146         fprintf(stderr, "%s: Waiting for 1 second...\n", __func__);
147         std::this_thread::sleep_for(std::chrono::seconds(kWaitSecondsPerIteration));
148         fprintf(stderr, "%s: Querying file size of guest trace...\n", __func__);
149         std::ifstream guestFile(guestFilename, std::ios::in | std::ios::binary | std::ios::ate);
150         std::streampos size = guestFile.tellg();
151 
152         if (!size) {
153             fprintf(stderr, "%s: No size, try again\n", __func__);
154             continue;
155         }
156 
157         if (size != currGuestSize) {
158             fprintf(stderr, "%s: Sized changed (%llu to %llu), try again\n", __func__,
159                     (unsigned long long)currGuestSize, (unsigned long long)size);
160             currGuestSize = size;
161             continue;
162         }
163 
164         ++numGoodGuestFileSizeIters;
165 
166         if (numGoodGuestFileSizeIters == kMinItersForGuestFileSize) {
167             fprintf(stderr, "%s: size is stable, continue saving\n", __func__);
168             good = true;
169             break;
170         }
171     }
172 
173     if (!good) {
174         fprintf(stderr, "%s: Timed out when waiting for guest file to stabilize, skipping combined trace saving.\n", __func__);
175         return;
176     }
177 
178     std::ifstream hostFile(hostFilename, std::ios_base::binary);
179     std::ifstream guestFile(guestFilename, std::ios_base::binary);
180     std::ofstream combinedFile(combinedFilename, std::ios::out | std::ios_base::binary);
181 
182     combinedFile << guestFile.rdbuf() << hostFile.rdbuf();
183 
184     fprintf(stderr, "%s: Wrote combined trace (%s)\n", __func__, combinedFilename);
185 }
186 
187 template <typename T>
varIntEncodingSize(T value)188 static inline size_t varIntEncodingSize(T value) {
189     // If value is <= 0 we must first sign extend to int64_t (see [1]).
190     // Finally we always cast to an unsigned value to to avoid arithmetic
191     // (sign expanding) shifts in the while loop.
192     // [1]: "If you use int32 or int64 as the type for a negative number, the
193     // resulting varint is always ten bytes long".
194     // - developers.google.com/protocol-buffers/docs/encoding
195     // So for each input type we do the following casts:
196     // uintX_t -> uintX_t -> uintX_t
197     // int8_t  -> int64_t -> uint64_t
198     // int16_t -> int64_t -> uint64_t
199     // int32_t -> int64_t -> uint64_t
200     // int64_t -> int64_t -> uint64_t
201     using MaybeExtendedType =
202         typename std::conditional<std::is_unsigned<T>::value, T, int64_t>::type;
203     using UnsignedType = typename std::make_unsigned<MaybeExtendedType>::type;
204 
205     MaybeExtendedType extended_value = static_cast<MaybeExtendedType>(value);
206     UnsignedType unsigned_value = static_cast<UnsignedType>(extended_value);
207 
208     size_t bytes = 0;
209 
210     while (unsigned_value >= 0x80) {
211         ++bytes;
212         unsigned_value >>= 7;
213     }
214 
215     return bytes + 1;
216 }
217 
218 template <typename T>
writeVarInt(T value,uint8_t * target)219 static inline uint8_t* writeVarInt(T value, uint8_t* target) {
220     // If value is <= 0 we must first sign extend to int64_t (see [1]).
221     // Finally we always cast to an unsigned value to to avoid arithmetic
222     // (sign expanding) shifts in the while loop.
223     // [1]: "If you use int32 or int64 as the type for a negative number, the
224     // resulting varint is always ten bytes long".
225     // - developers.google.com/protocol-buffers/docs/encoding
226     // So for each input type we do the following casts:
227     // uintX_t -> uintX_t -> uintX_t
228     // int8_t  -> int64_t -> uint64_t
229     // int16_t -> int64_t -> uint64_t
230     // int32_t -> int64_t -> uint64_t
231     // int64_t -> int64_t -> uint64_t
232     using MaybeExtendedType =
233         typename std::conditional<std::is_unsigned<T>::value, T, int64_t>::type;
234     using UnsignedType = typename std::make_unsigned<MaybeExtendedType>::type;
235 
236     MaybeExtendedType extended_value = static_cast<MaybeExtendedType>(value);
237     UnsignedType unsigned_value = static_cast<UnsignedType>(extended_value);
238 
239     while (unsigned_value >= 0x80) {
240         *target++ = static_cast<uint8_t>(unsigned_value) | 0x80;
241         unsigned_value >>= 7;
242     }
243     *target = static_cast<uint8_t>(unsigned_value);
244     return target + 1;
245 }
246 
sProcessTrace(const std::vector<char> & trace)247 static std::vector<char> sProcessTrace(const std::vector<char>& trace) {
248     std::vector<char> res;
249 
250     ::perfetto::protos::Trace pbtrace;
251     std::string traceStr(trace.begin(), trace.end());
252 
253     if (pbtrace.ParseFromString(traceStr)) {
254     } else {
255         fprintf(stderr, "%s: Failed to parse protobuf as a string\n", __func__);
256         return res;
257     }
258 
259     fprintf(stderr, "%s: postprocessing trace with guest time diff of %llu\n", __func__,
260             (unsigned long long)sTraceConfig.guestTimeDiff);
261 
262     for (int i = 0; i < pbtrace.packet_size(); ++i) {
263         // Process one trace packet.
264         auto* packet = pbtrace.mutable_packet(i);
265         if (packet->has_timestamp()) {
266             packet->set_timestamp(packet->timestamp() + sTraceConfig.guestTimeDiff);
267         }
268     }
269 
270     std::string traceAfter;
271     pbtrace.SerializeToString(&traceAfter);
272 
273     std::vector<char> res2(traceAfter.begin(), traceAfter.end());
274     return res2;
275 }
276 
disableTracing()277 PERFETTO_TRACING_ONLY_EXPORT void disableTracing() {
278     if (sTracingSession) {
279         sTraceConfig.tracingDisabled = true;
280         sTracingSession->StopBlocking();
281         std::vector<char> trace_data(sTracingSession->ReadTraceBlocking());
282 
283         std::vector<char> processed =
284             sProcessTrace(trace_data);
285 
286         fprintf(stderr, "%s: Tracing ended================================================================================\n", __func__);
287         fprintf(stderr, "%s: Saving trace to disk. Configuration:\n", __func__);
288         fprintf(stderr, "%s: host filename: %s\n", __func__, sTraceConfig.hostFilename);
289         fprintf(stderr, "%s: guest filename: %s\n", __func__, sTraceConfig.guestFilename);
290         fprintf(stderr, "%s: combined filename: %s\n", __func__, sTraceConfig.combinedFilename);
291 
292         fprintf(stderr, "%s: Saving host trace first...\n", __func__);
293 
294         // Write the trace into a file.
295         std::ofstream output;
296         output.open(sTraceConfig.hostFilename, std::ios::out | std::ios::binary);
297         output.write(&processed[0], processed.size());
298         output.close();
299         sTracingSession.reset();
300 
301         fprintf(stderr, "%s: Saving host trace first...(done)\n", __func__);
302 
303         if (!sTraceConfig.guestFilename || !sTraceConfig.combinedFilename) {
304             fprintf(stderr, "%s: skipping guest combined trace, "
305                             "either guest file name (%p) not specified or "
306                             "combined file name (%p) not specified\n", __func__,
307                     sTraceConfig.guestFilename,
308                     sTraceConfig.combinedFilename);
309             return;
310         }
311 
312         std::thread saveThread(asyncTraceSaveFunc);
313         saveThread.detach();
314     }
315 }
316 
beginTrace(const char * eventName)317 PERFETTO_TRACING_ONLY_EXPORT void beginTrace(const char* eventName) {
318     TRACE_EVENT_BEGIN("gfx", ::perfetto::StaticString{eventName});
319 }
320 
endTrace()321 PERFETTO_TRACING_ONLY_EXPORT void endTrace() {
322     TRACE_EVENT_END("gfx");
323 }
324 
traceCounter(const char * name,int64_t value)325 PERFETTO_TRACING_ONLY_EXPORT void traceCounter(const char* name, int64_t value) {
326     // TODO: What this really needs until its supported in the official sdk:
327     // a. a static global to track uuids and names for counters
328     // b. track objects generated dynamically
329     // c. setting the descriptor of these track objects
330     // if (CC_LIKELY(sTraceConfig.tracingDisabled)) return;
331     // TRACE_COUNTER("gfx", ::perfetto::StaticString{name}, value);
332 }
333 
setGuestTime(uint64_t t)334 PERFETTO_TRACING_ONLY_EXPORT void setGuestTime(uint64_t t) {
335     virtualdeviceperfetto::setTraceConfig([t](virtualdeviceperfetto::VirtualDeviceTraceConfig& config) {
336         // can only be set before tracing
337         if (!config.tracingDisabled) {
338             return;
339         }
340         config.guestStartTime = t;
341         config.hostStartTime = (uint64_t)(::perfetto::base::GetWallTimeNs().count());
342         config.guestTimeDiff = config.guestStartTime - config.hostStartTime;
343     });
344 }
345 
346 
347 } // namespace perfetto
348