1 : // Copyright 2012 Google Inc. All Rights Reserved.
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 SYZYGY_TRACE_PROTOCOL_CALL_TRACE_DEFS_H_
16 : #define SYZYGY_TRACE_PROTOCOL_CALL_TRACE_DEFS_H_
17 :
18 : #include <stdint.h>
19 : #include <windows.h>
20 : #include <wmistr.h>
21 : #include <evntrace.h> // NOLINT - wmistr must precede envtrace.h
22 : #include <vector>
23 :
24 : #include "base/strings/string_piece.h"
25 : #include "syzygy/common/assertions.h"
26 : #include "syzygy/trace/common/clock.h"
27 :
28 : // ID for the call trace provider.
29 : extern const GUID kCallTraceProvider;
30 :
31 : // Class of trace provider events.
32 : extern const GUID kCallTraceEventClass;
33 :
34 : // GUID for the kernel trace control interface.
35 : extern const GUID kSystemTraceControlGuid;
36 :
37 : // This is the absolute minimum number of buffers we will allow, across all
38 : // CPUs.
39 : extern const size_t kMinEtwBuffers;
40 :
41 : // This is the minimum number of buffers per CPU we'll allow.
42 : extern const size_t kMinEtwBuffersPerProcessor;
43 :
44 : // Max buffers will be min buffers * kEtwBufferMultiplier.
45 : extern const size_t kEtwBufferMultiplier;
46 :
47 : // The set of flags to use when logging trace events via ETW.
48 : extern const int kDefaultEtwTraceFlags;
49 :
50 : // The set of flags to use when logging kernel events via ETW.
51 : extern const int kDefaultEtwKernelFlags;
52 :
53 : // RPC protocol and endpoint.
54 : extern const char kSyzygyRpcInstanceIdEnvVar[];
55 : void GetSyzygyCallTraceRpcProtocol(std::wstring* protocol);
56 : void GetSyzygyCallTraceRpcEndpoint(const base::StringPiece16& id,
57 : std::wstring* endpoint);
58 : void GetSyzygyCallTraceRpcMutexName(const base::StringPiece16& id,
59 : std::wstring* mutex_name);
60 : void GetSyzygyCallTraceRpcEventName(const base::StringPiece16& id,
61 : std::wstring* event_name);
62 :
63 : // Environment variable used to indicate that an RPC session is mandatory.
64 : extern const char kSyzygyRpcSessionMandatoryEnvVar[];
65 :
66 : // This must be bumped anytime the file format is changed.
67 : enum {
68 : TRACE_VERSION_HI = 1,
69 : TRACE_VERSION_LO = 4,
70 : };
71 :
72 : enum TraceEventType {
73 : // Header prefix for a "page" of call trace events.
74 : TRACE_PAGE_HEADER,
75 : // The actual events are below.
76 : TRACE_PROCESS_STARTED = 10,
77 : TRACE_PROCESS_ENDED,
78 : TRACE_ENTER_EVENT,
79 : TRACE_EXIT_EVENT,
80 : TRACE_PROCESS_ATTACH_EVENT,
81 : TRACE_PROCESS_DETACH_EVENT,
82 : TRACE_THREAD_ATTACH_EVENT,
83 : TRACE_THREAD_DETACH_EVENT,
84 : TRACE_MODULE_EVENT,
85 : TRACE_BATCH_ENTER,
86 : TRACE_BATCH_INVOCATION,
87 : TRACE_THREAD_NAME,
88 : TRACE_INDEXED_FREQUENCY,
89 : TRACE_DYNAMIC_SYMBOL,
90 : TRACE_SAMPLE_DATA,
91 : TRACE_FUNCTION_NAME_TABLE_ENTRY,
92 : TRACE_STACK_TRACE,
93 : TRACE_DETAILED_FUNCTION_CALL,
94 : TRACE_COMMENT,
95 : TRACE_PROCESS_HEAP,
96 : };
97 :
98 : // All traces are emitted at this trace level.
99 : const UCHAR CALL_TRACE_LEVEL = TRACE_LEVEL_INFORMATION;
100 :
101 : enum TraceEventFlags {
102 : // Trace function entry.
103 : TRACE_FLAG_ENTER = 0x0001,
104 : // Trace function exit.
105 : TRACE_FLAG_EXIT = 0x0002,
106 : // Capture stack traces on entry and exit.
107 : TRACE_FLAG_STACK_TRACES = 0x0004,
108 : // Trace DLL load/unload events.
109 : TRACE_FLAG_LOAD_EVENTS = 0x0008,
110 : // Trace DLL thread events.
111 : TRACE_FLAG_THREAD_EVENTS = 0x0010,
112 : // Batch entry traces.
113 : TRACE_FLAG_BATCH_ENTER = 0x0020,
114 : };
115 :
116 : // Max depth of stack trace captured on entry/exit.
117 : const size_t kMaxTraceDepth = 32;
118 :
119 : typedef const void* RetAddr;
120 : typedef const void* FuncAddr;
121 : typedef const void* ModuleAddr;
122 : typedef DWORD ArgumentWord;
123 : typedef DWORD RetValueWord;
124 : typedef void* SessionHandle;
125 :
126 : // A prefix for each trace record on disk.
127 : struct RecordPrefix {
128 : // The timestamp of the trace event.
129 : uint64_t timestamp;
130 :
131 : // The size of the record, in bytes;
132 : uint32_t size;
133 :
134 : // The type of trace record. Will be a value from the TraceEventType
135 : // enumeration.
136 : uint16_t type;
137 :
138 : // If the call trace service aggregates all trace records to a single
139 : // file, instead of a file per process, then it's possible that a
140 : // single file could contain traces produced by multiple versions of
141 : // the client library.
142 : struct {
143 : uint8_t hi;
144 : uint8_t lo;
145 : } version;
146 : };
147 : COMPILE_ASSERT_IS_POD_OF_SIZE(RecordPrefix, 16);
148 :
149 : // This structure is written at the beginning of a call trace file. If the
150 : // format of this trace file changes the server version must be increased.
151 : struct TraceFileHeader {
152 : // Everything in this header up to and including the header_size field should
153 : // not be changed in order, layout or alignment. This allows the beginning of
154 : // the header to be read across all trace file versions. If adding a new
155 : // fixed length field, do so immediately prior to blob_data. If adding a new
156 : // variable length field, append it to blob data updating the comment below,
157 : // and both the reading and writing of TraceFileHeader.
158 :
159 : // The "magic-number" identifying this as a Syzygy call-trace file.
160 : // In a valid trace file this will be "SZGY".
161 : typedef char Signature[4];
162 :
163 : // A canonical value for the signature.
164 : static const Signature kSignatureValue;
165 :
166 : // A signature is at the start of the trace file header.
167 : Signature signature;
168 :
169 : // The version of the call trace service which recorded this trace file.
170 : struct {
171 : uint16_t lo;
172 : uint16_t hi;
173 : } server_version;
174 :
175 : // The number of bytes in the header. This is the size of this structure
176 : // plus the length of the blob.
177 : uint32_t header_size;
178 :
179 : // Nothing above this point in the header can change in order to maintain
180 : // the ability to parse the basic header with the version number. This by
181 : // itself doesn't guarantee backwards compatibility, but it does ensure that
182 : // we can detect trace files generated by older versions of the toolchain.
183 :
184 : // The block size used when writing the file to disk. The header and
185 : // all segments are padded and byte aligned to this block size.
186 : uint32_t block_size;
187 :
188 : // The id of the process being traced.
189 : uint32_t process_id;
190 :
191 : // The base address at which the executable module was loaded when the
192 : // trace file was created.
193 : uint32_t module_base_address;
194 :
195 : // The size of the executable module.
196 : uint32_t module_size;
197 :
198 : // The checksum of the executable module.
199 : uint32_t module_checksum;
200 :
201 : // The timestamp of the executable module.
202 : uint32_t module_time_date_stamp;
203 :
204 : // System information.
205 : OSVERSIONINFOEX os_version_info;
206 : SYSTEM_INFO system_info;
207 : MEMORYSTATUSEX memory_status;
208 :
209 : // Clock information. This lets us convert from timestamps (both TSC and
210 : // ticks) to absolute system times. It also contains a timestamp for the
211 : // header itself.
212 : trace::common::ClockInfo clock_info;
213 :
214 : // The header is required to store multiple variable length fields. We do
215 : // this via a blob mechanism. The header contains a single binary blob at the
216 : // end, whose length in bytes) is encoded via blob_length.
217 : //
218 : // Currently, the header stores the following variable length fields (in
219 : // the order indicated):
220 : //
221 : // 1. The path to the instrumented module, a NULL terminated wide string.
222 : // 2. The command line for the process, a NULL terminated wide string.
223 : // 3. The environment string for the process, an array of wide chars
224 : // terminated by a double NULL (individual environment variables are
225 : // separated by single NULLs).
226 :
227 : // This stores the variable length data, concatenated. This should be pointer
228 : // aligned so that PODs with alignment constraints embedded in the blob can be
229 : // read directly from a header loaded into memory.
230 : uint8_t blob_data[1];
231 : };
232 : COMPILE_ASSERT_IS_POD(TraceFileHeader);
233 :
234 : // Written at the beginning of a call trace file segment. Each call trace file
235 : // segment has a length, which on-disk is rounded up to the block_size, as
236 : // recorded in the TraceFileHeader. Within a call trace segment, there are one
237 : // or more records, each prefixed with a RecordPrefix, which describes the
238 : // length and type of the data to follow.
239 : struct TraceFileSegmentHeader {
240 : // Type identifiers used for these headers.
241 : enum { kTypeId = TRACE_PAGE_HEADER };
242 :
243 : // The identity of the thread that is reporting in this segment
244 : // of the trace file.
245 : uint32_t thread_id;
246 :
247 : // The number of data bytes in this segment of the trace file. This
248 : // value does not include the size of the record prefix nor the size
249 : // of the segment header.
250 : uint32_t segment_length;
251 : };
252 : COMPILE_ASSERT_IS_POD(TraceFileSegmentHeader);
253 :
254 : // The structure traced on function entry or exit.
255 : template<int TypeId>
256 : struct TraceEnterExitEventDataTempl {
257 : enum { kTypeId = TypeId };
258 : RetAddr retaddr;
259 : FuncAddr function;
260 : };
261 :
262 : typedef TraceEnterExitEventDataTempl<TRACE_ENTER_EVENT> TraceEnterEventData;
263 : typedef TraceEnterExitEventDataTempl<TRACE_EXIT_EVENT> TraceExitEventData;
264 : typedef TraceEnterEventData TraceEnterExitEventData;
265 : COMPILE_ASSERT_IS_POD(TraceEnterEventData);
266 : COMPILE_ASSERT_IS_POD(TraceExitEventData);
267 : COMPILE_ASSERT_IS_POD(TraceEnterExitEventData);
268 :
269 : // The structure written for each loaded module when module event tracing is
270 : // enabled.
271 : struct TraceModuleData {
272 : ModuleAddr module_base_addr;
273 : size_t module_base_size;
274 : uint32_t module_checksum;
275 : uint32_t module_time_date_stamp;
276 : wchar_t module_name[256];
277 : wchar_t module_exe[MAX_PATH];
278 : };
279 : COMPILE_ASSERT_IS_POD(TraceModuleData);
280 :
281 : // This is for storing environment string information. Each environment string
282 : // consists of a pair of strings, the key and the value. Certain special
283 : // strings have empty keys.
284 E : typedef std::vector<std::pair<std::wstring, std::wstring>>
285 : TraceEnvironmentStrings;
286 :
287 : // Describes the system information and environment in which a process is
288 : // running.
289 : struct TraceSystemInfo {
290 : OSVERSIONINFOEX os_version_info;
291 : SYSTEM_INFO system_info;
292 : MEMORYSTATUSEX memory_status;
293 : trace::common::ClockInfo clock_info;
294 : TraceEnvironmentStrings environment_strings;
295 : };
296 :
297 : // The structure traced for batch entry traces.
298 : struct TraceBatchEnterData {
299 : enum { kTypeId = TRACE_BATCH_ENTER };
300 :
301 : // The thread ID from which these traces originate. This can differ
302 : // from the logging thread ID when a process exits, and the exiting
303 : // thread flushes the trace buffers from its expired brethren.
304 : DWORD thread_id;
305 :
306 : // Number of function entries.
307 : size_t num_calls;
308 :
309 : // Back-to-back entry events.
310 : TraceEnterEventData calls[1];
311 : };
312 : COMPILE_ASSERT_IS_POD(TraceBatchEnterData);
313 :
314 : enum InvocationInfoFlags {
315 : // If this bit is set in InvocationInfo flags, the caller is a dynamic
316 : // symbol id, and caller_offset is the offset of the return site, relative to
317 : // the start of the caller's symbol.
318 : kCallerIsSymbol = 0x01,
319 : // If this bit is set in InvocationInfo flags, the function is a dynamic
320 : // symbol id, instead of an address.
321 : kFunctionIsSymbol = 0x02,
322 : };
323 :
324 : // This is the data recorded for each distinct caller/function
325 : // pair by the profiler.
326 : struct InvocationInfo {
327 : union {
328 : RetAddr caller;
329 : uint32_t caller_symbol_id;
330 : };
331 : union {
332 : FuncAddr function;
333 : uint32_t function_symbol_id;
334 : };
335 : size_t num_calls;
336 : uint32_t flags : 8;
337 : uint32_t caller_offset : 24;
338 : uint64_t cycles_min;
339 : uint64_t cycles_max;
340 : uint64_t cycles_sum;
341 : };
342 : COMPILE_ASSERT_IS_POD(InvocationInfo);
343 :
344 : struct TraceBatchInvocationInfo {
345 : enum { kTypeId = TRACE_BATCH_INVOCATION };
346 :
347 : // TODO(siggi): Perhaps the batch should carry the time resolution for
348 : // the invocation data?
349 :
350 : // Back to back entries, as many as our enclosing record's size allows for.
351 : InvocationInfo invocations[1];
352 : };
353 : COMPILE_ASSERT_IS_POD(TraceBatchInvocationInfo);
354 :
355 : struct TraceThreadNameInfo {
356 : enum { kTypeId = TRACE_THREAD_NAME };
357 : // In fact as many as our enclosing record's size allows for,
358 : // zero terminated.
359 : char thread_name[1];
360 : };
361 : COMPILE_ASSERT_IS_POD(TraceThreadNameInfo);
362 :
363 : struct TraceIndexedFrequencyData {
364 : enum { kTypeId = TRACE_INDEXED_FREQUENCY };
365 :
366 : // This is used to tie the data to a particular module, which has already
367 : // been reported via a TraceModuleData struct.
368 : ModuleAddr module_base_addr;
369 : size_t module_base_size;
370 : uint32_t module_checksum;
371 : uint32_t module_time_date_stamp;
372 :
373 : // The number of entries being reported. It is up to the instrumentation to
374 : // output any other metadata that is required to map an index to an address.
375 : uint32_t num_entries;
376 :
377 : // The number of columns for each record. Each column entry has the data sized
378 : // specified by |frequency_size|.
379 : uint32_t num_columns;
380 :
381 : // The type of data contained in this frequency record. This should be one of
382 : // the data-types defined in IndexedFrequencyData::DataType.
383 : uint8_t data_type;
384 :
385 : // The size of the frequency reports: 1, 2 or 4 bytes.
386 : uint8_t frequency_size;
387 :
388 : // In fact, there are frequency_size * num_basic_blocks bytes that follow.
389 : uint8_t frequency_data[1];
390 : };
391 : COMPILE_ASSERT_IS_POD(TraceIndexedFrequencyData);
392 :
393 : struct TraceDynamicSymbol {
394 : enum { kTypeId = TRACE_DYNAMIC_SYMBOL };
395 :
396 : // The symbol's ID, unique per process.
397 : uint32_t symbol_id;
398 : // In fact as many as our enclosing record's size allows for,
399 : // zero terminated.
400 : char symbol_name[1];
401 : };
402 : COMPILE_ASSERT_IS_POD(TraceDynamicSymbol);
403 :
404 : struct TraceSampleData {
405 : enum { kTypeId = TRACE_SAMPLE_DATA };
406 :
407 : // This is used to tie the data to a particular module, which has already
408 : // been reported via a TraceModuleData struct.
409 : ModuleAddr module_base_addr;
410 : size_t module_size;
411 : uint32_t module_checksum;
412 : uint32_t module_time_date_stamp;
413 :
414 : // The size of each bucket in the sample data. This will be a power of 2 in
415 : // size.
416 : uint32_t bucket_size;
417 :
418 : // The beginning of the sampling buckets as an address in the image.
419 : // This will be aligned with the bucket size.
420 : ModuleAddr bucket_start;
421 :
422 : // The number of buckets in the sample data.
423 : uint32_t bucket_count;
424 :
425 : // The time when the trace started and ended.
426 : uint64_t sampling_start_time;
427 : uint64_t sampling_end_time;
428 :
429 : // The sampling interval, expressed in clock cycles.
430 : uint64_t sampling_interval;
431 :
432 : // There are actually |bucket_count| buckets that follow.
433 : uint32_t buckets[1];
434 : };
435 : COMPILE_ASSERT_IS_POD(TraceSampleData);
436 :
437 : // Records a functions name and its assined ID.
438 : struct TraceFunctionNameTableEntry {
439 : enum { kTypeId = TRACE_FUNCTION_NAME_TABLE_ENTRY };
440 :
441 : // The assigned ID of this function.
442 : uint32_t function_id;
443 :
444 : // The length of the function name.
445 : uint32_t name_length;
446 :
447 : // The name of this function. This is actually of length |name_length|,
448 : // which doesn't need to include null termination.
449 : char name[1];
450 : };
451 : COMPILE_ASSERT_IS_POD(TraceFunctionNameTableEntry);
452 :
453 : // Records an unsymbolized stack trace. This is referred to by other
454 : // records, which put it in appropriate context.
455 : struct TraceStackTrace {
456 : enum { kTypeId = TRACE_STACK_TRACE };
457 :
458 : // The ID of this stack trace. This is computed as a hash of the stack trace
459 : // and is unique for a given set of load addresses (ie: for a process).
460 : uint32_t stack_trace_id;
461 :
462 : // The number of frames in the stack trace.
463 : uint32_t num_frames;
464 :
465 : // The frames of the stack trace. There are actually |num_frames| frames
466 : // in total.
467 : void* frames[1];
468 : };
469 :
470 : // Records detailed information about a call to a function.
471 : struct TraceDetailedFunctionCall {
472 : enum { kTypeId = TRACE_DETAILED_FUNCTION_CALL };
473 :
474 : // The timestamp of the funtion call.
475 : uint64_t timestamp;
476 :
477 : // The ID of the function. This is an entry in the global
478 : // function table.
479 : uint32_t function_id;
480 :
481 : // The ID of the stack trace leading to the function call.
482 : uint32_t stack_trace_id;
483 :
484 : // The size of the argument data.
485 : uint32_t argument_data_size;
486 :
487 : // The blob of argument data. This is actually of size
488 : // |argument_data_size|. This is laid out as follows:
489 : // uint32_t argument_count
490 : // uint32_t argument_length_0
491 : // uint32_t argument_length_1
492 : // ...
493 : // uint8_t argument_data_0
494 : // uint8_t argument_data_1
495 : // ...
496 : // The content and interpretation of this data is up to the
497 : // individual producer and consumer.
498 : uint8_t argument_data[1];
499 : };
500 : COMPILE_ASSERT_IS_POD(TraceDetailedFunctionCall);
501 :
502 : // Records a comment in a trace file. These are output via the call-trace
503 : // service and act as delimiters in a call-trace log.
504 : struct TraceComment {
505 : enum { kTypedId = TRACE_COMMENT };
506 :
507 : // The size of the comment in bytes.
508 : uint32_t comment_size;
509 :
510 : // Actually of size |comment_size|. A UTF8 encoded string.
511 : char comment[1];
512 : };
513 :
514 : // Records the address of the process heap.
515 : struct TraceProcessHeap {
516 : enum { kTypeId = TRACE_PROCESS_HEAP };
517 :
518 : uint32_t process_heap;
519 : };
520 : COMPILE_ASSERT_IS_POD(TraceProcessHeap);
521 :
522 : #endif // SYZYGY_TRACE_PROTOCOL_CALL_TRACE_DEFS_H_
|