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