1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Buffer layout.
6 // This is an internal header between trace-engine and trace-provider.
7 // It may also be used by various tests.
8 
9 #pragma once
10 
11 #include <assert.h>
12 #include <stdint.h>
13 #include <trace-engine/context.h>
14 
15 namespace trace {
16 namespace internal {
17 
18 // This header provides framing information about the buffer, for use in
19 // implementing circular buffering and double(streaming) buffering.
20 //
21 // Writing to the buffer has conceptually three modes:
22 // oneshot, circular, streaming.
23 //
24 // Buffers are passed from Trace Manager to Trace Provider in vmos.
25 // How the buffer is treated depends on the writing mode.
26 // For "oneshot" mode the vmo is one big simple buffer.
27 //   Using one big buffer means durable and non-durable records all share the
28 //   same buffer.
29 //   For simplicity in the code, oneshot mode uses rolling buffer 0.
30 // For "circular" and "streaming" buffering modes, the vmo is treated as a
31 // "virtual buffer" and is split into three logical parts:
32 //   - one buffer for "durable" records
33 //   - two buffers, labeled 0 and 1, for "non-durable" records, called
34 //     "rolling buffers"
35 // Writing switches back and forth between the two rolling buffers as each
36 // fills. Streaming buffering differs from circular buffering in that the Trace
37 // Manager is involved in saving each rolling buffer as it fills.
38 // Besides consistency, a nice property of using two separate buffers for
39 // circular mode is that, because records are variable sized, there are no
40 // issues trying to find the "first" non-durable record in the complete virtual
41 // buffer after a wrap: It's always the first record of the other rolling
42 // buffer.
43 //
44 // To help preserve data integrity tracing stops when the durable buffer fills,
45 // even in circular mode.
46 // TODO(dje): Relax this restriction, and accept potentially more lost data.
47 //
48 // Durable records:
49 // - initialization record
50 // - string table
51 // - thread table
52 // TODO(dje): Move initialization record to header?
53 //
54 // Non-durable records:
55 // - everything else
56 //
57 // The total physical buffer is laid out as follows (without gaps):
58 // - header
59 // - durable buffer (empty in oneshot mode)
60 // - non-durable buffer 0
61 // - non-durable buffer 1 (empty in oneshot mode)
62 //
63 // It is an invariant that:
64 // oneshot:
65 //   total_size == header + rolling_buffer_size
66 // circular/streaming:
67 //   total_size == header + durable_buffer_size + 2 * rolling_buffer_size
68 //
69 // All buffer sizes must be a multiple of 8 as all records are a multiple of 8.
70 
71 struct trace_buffer_header {
72     // Standard magic number field.
73     uint64_t magic;
74 #define TRACE_BUFFER_HEADER_MAGIC ((uint64_t) 0x627566ee68656164ull)
75 
76     uint16_t version;
77 #define TRACE_BUFFER_HEADER_V0 ((uint16_t) 0)
78 
79     // One of |trace_buffering_mode_t|.
80     uint8_t buffering_mode;
81 
82     // For alignment and future concerns.
83     uint8_t reserved1;
84 
85     // A count of the number of times writing wrapped.
86     // If zero then writing didn't wrap. If non-zero then |wrapped_count % 2|
87     // is the buffer number where writing finished.
88     uint32_t wrapped_count;
89 
90     // The size of the buffer in bytes, including this header.
91     // In other words this is the size of the vmo.
92     uint64_t total_size;
93 
94     // The size in bytes of the durable record buffer.
95     // This is zero in oneshot mode.
96     uint64_t durable_buffer_size;
97 
98     // The size in bytes of each of the rolling record buffers.
99     uint64_t rolling_buffer_size;
100 
101     // The offset, from the first data byte, to the end of recorded durable
102     // data. This starts at zero and is not written to while writing the buffer
103     // is active. This remains zero in oneshot mode (since there is no separate
104     // buffer for durable records). It is written to when the buffer fills or
105     // when tracing is stopped.
106     uint64_t durable_data_end;
107 
108     // The offset, from the first data byte, to the end of recorded data.
109     // In oneshot mode only [0] is used. This starts at zero and is not written
110     // to while writing the buffer is active. It is written to when the buffer
111     // fills or when tracing is stopped.
112     uint64_t rolling_data_end[2];
113 
114     // Total number of records dropped thus far.
115     uint64_t num_records_dropped;
116 
117     // The header is padded out to a size of 128 to provide room for growth,
118     // and to simplify internal buffer size calcs.
119     // The remainder of the header is reserved.
120     uint64_t reserved[7];
121 };
122 
123 static_assert(sizeof(trace_buffer_header) == 128, "");
124 
125 } // namespace internal
126 } // namespace trace
127 
128 // Update the buffer header and snapshot a copy of it.
129 // This is only intended to be used for testing purposes.
130 //
131 // This function is not thread-safe relative to the collected data, and
132 // assumes tracing is stopped or at least paused.
133 void trace_context_snapshot_buffer_header(
134     trace_prolonged_context_t* context,
135     ::trace::internal::trace_buffer_header* header);
136