1 : // Copyright 2014 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 : // Declares a FunctionCallLogger class. This contains functionality for
16 : // logging detailed function call records via the call-trace service.
17 :
18 : #ifndef SYZYGY_AGENT_MEMPROF_FUNCTION_CALL_LOGGER_H_
19 : #define SYZYGY_AGENT_MEMPROF_FUNCTION_CALL_LOGGER_H_
20 :
21 : #include <set>
22 :
23 : #include "syzygy/agent/memprof/parameters.h"
24 : #include "syzygy/trace/client/rpc_session.h"
25 :
26 : namespace agent {
27 : namespace memprof {
28 :
29 : class FunctionCallLogger {
30 : public:
31 : // Forward declarations.
32 : struct NoArgument;
33 : template<typename ArgType> struct ArgumentSerializer;
34 :
35 : typedef trace::client::TraceFileSegment TraceFileSegment;
36 :
37 : // Consructor.
38 : // @param session The call-trace session to log to.
39 : // @param segment The segment to write to.
40 : explicit FunctionCallLogger(trace::client::RpcSession* session);
41 :
42 : // Given a function name returns it's ID. If this is the first time seeing
43 : // a given function name then emits a record to the call-trace buffer.
44 : // @param function_name The name of the function.
45 : // @returns the ID of the function.
46 : uint32_t GetFunctionId(TraceFileSegment* segment,
47 : const std::string& function_name);
48 :
49 : // Gets a stack ID for the current stack. The behaviour of this function
50 : // depends on the stack_trace_tracking mode. If disabled, this always
51 : // returns 0. If enabled, this returns the actual ID of the current stack.
52 : // If 'emit' mode is enabled, this will also keep track of already emitted
53 : // stack IDs and emit the stack the first time it's encountered.
54 : // @returns the ID of the current stack trace.
55 : uint32_t GetStackTraceId(TraceFileSegment* segment);
56 :
57 : // Emits a detailed function call event with a variable number of arguments.
58 : // @tparam ArgTypeN The type of the optional Nth argument.
59 : // @param function_id The ID of the function that was called.
60 : // @param stack_trace_id The ID of the stack trace where the function was
61 : // called.
62 : // @param argN The value of the optional Nth argument.
63 : template <typename ArgType0 = NoArgument,
64 : typename ArgType1 = NoArgument,
65 : typename ArgType2 = NoArgument,
66 : typename ArgType3 = NoArgument,
67 : typename ArgType4 = NoArgument,
68 : typename ArgType5 = NoArgument>
69 : void EmitDetailedFunctionCall(TraceFileSegment* segment,
70 : uint32_t function_id,
71 : uint32_t stack_trace_id,
72 : ArgType0 arg0 = NoArgument(),
73 : ArgType1 arg1 = NoArgument(),
74 : ArgType2 arg2 = NoArgument(),
75 : ArgType3 arg3 = NoArgument(),
76 : ArgType4 arg4 = NoArgument(),
77 : ArgType5 arg5 = NoArgument());
78 :
79 : // @name Accessors and mutators.
80 : // @{
81 : StackTraceTracking stack_trace_tracking() const {
82 : return stack_trace_tracking_;
83 : }
84 E : void set_stack_trace_tracking(StackTraceTracking tracking) {
85 E : stack_trace_tracking_ = tracking;
86 E : }
87 : bool serialize_timestamps() const {
88 : return serialize_timestamps_;
89 : }
90 E : void set_serialize_timestamps(bool serialize_timestamps) {
91 E : serialize_timestamps_ = serialize_timestamps;
92 E : }
93 : // @}
94 :
95 : // @returns a unique serial number for this function call logger.
96 : // @note This is for unittesting purposes.
97 E : uint32_t serial() const { return serial_; }
98 :
99 : protected:
100 : // Flushes the provided segment, and gets a new one.
101 : bool FlushSegment(TraceFileSegment* segment);
102 :
103 : // The stack-trace tracking mode. Default to kTrackingNone.
104 : StackTraceTracking stack_trace_tracking_;
105 :
106 : // Whether or not timestamps are being serialized.
107 : bool serialize_timestamps_;
108 :
109 : // The RPC session events are being written to.
110 : trace::client::RpcSession* session_;
111 :
112 : // A lock that is used for synchronizing access to internals.
113 : base::Lock lock_;
114 :
115 : // The counter to use for serialized timestamps. Only used if
116 : // |serialized_timestamps_| is true.
117 : uint64_t call_counter_; // Under lock_.
118 :
119 : // A map of known function names and their IDs. This is used for making the
120 : // call-trace format more compact.
121 : typedef std::map<std::string, uint32_t> FunctionIdMap;
122 : FunctionIdMap function_id_map_; // Under lock_.
123 :
124 : // A set of stack traces whose IDs have already been emitted. This is only
125 : // maintained if stack_trace_tracking_ is set to 'kTrackingEmit'.
126 : typedef std::set<uint32_t> StackIdSet;
127 : StackIdSet emitted_stack_ids_; // Under lock_.
128 :
129 : // A unique serial number generated at construction time. For unittesting.
130 : uint32_t serial_;
131 :
132 : private:
133 : DISALLOW_COPY_AND_ASSIGN(FunctionCallLogger);
134 : };
135 :
136 : // Used to indicate the lack of an argument in the detailed
137 : // function call reporting helper.
138 : struct FunctionCallLogger::NoArgument {};
139 :
140 : // Helper for serializing argument contents.
141 : template<typename ArgType>
142 : struct FunctionCallLogger::ArgumentSerializer {
143 E : size_t size() const {
144 E : return sizeof(ArgType);
145 E : }
146 E : void serialize(ArgType argument, uint8_t* buffer) {
147 E : ::memcpy(buffer, &argument, sizeof(ArgType));
148 E : }
149 : };
150 :
151 : // A no-op serializer for unused arguments.
152 : template<> struct FunctionCallLogger::ArgumentSerializer<
153 : FunctionCallLogger::NoArgument> {
154 E : size_t size() const {
155 E : return 0;
156 E : }
157 E : void serialize(NoArgument argument, uint8_t* buffer) { return; }
158 : };
159 :
160 : // Implementation off the detailed function call logger. Populates a
161 : // TraceDetailedFunctionCall buffer with variable length encodings of
162 : // the arguments. Arguments are serialized using the ArgumentSerializer
163 : // helper.
164 : template <typename ArgType0,
165 : typename ArgType1,
166 : typename ArgType2,
167 : typename ArgType3,
168 : typename ArgType4,
169 : typename ArgType5>
170 : void FunctionCallLogger::EmitDetailedFunctionCall(TraceFileSegment* segment,
171 : uint32_t function_id,
172 : uint32_t stack_trace_id,
173 : ArgType0 arg0,
174 : ArgType1 arg1,
175 : ArgType2 arg2,
176 : ArgType3 arg3,
177 : ArgType4 arg4,
178 E : ArgType5 arg5) {
179 E : DCHECK_NE(static_cast<TraceFileSegment*>(nullptr), segment);
180 E : size_t args_count = 0;
181 E : size_t args_size = 0;
182 :
183 E : size_t arg_size0 = ArgumentSerializer<ArgType0>().size();
184 E : args_count += arg_size0 > 0 ? 1 : 0;
185 E : args_size += arg_size0;
186 :
187 E : size_t arg_size1 = ArgumentSerializer<ArgType1>().size();
188 E : args_count += arg_size1 > 0 ? 1 : 0;
189 E : args_size += arg_size1;
190 :
191 E : size_t arg_size2 = ArgumentSerializer<ArgType2>().size();
192 E : args_count += arg_size2 > 0 ? 1 : 0;
193 E : args_size += arg_size2;
194 :
195 E : size_t arg_size3 = ArgumentSerializer<ArgType3>().size();
196 E : args_count += arg_size3 > 0 ? 1 : 0;
197 E : args_size += arg_size3;
198 :
199 E : size_t arg_size4 = ArgumentSerializer<ArgType4>().size();
200 E : args_count += arg_size4 > 0 ? 1 : 0;
201 E : args_size += arg_size4;
202 :
203 E : size_t arg_size5 = ArgumentSerializer<ArgType5>().size();
204 E : args_count += arg_size5 > 0 ? 1 : 0;
205 E : args_size += arg_size5;
206 :
207 E : if (args_size > 0)
208 E : args_size += (args_count + 1) * sizeof(uint32_t);
209 E : size_t data_size = FIELD_OFFSET(TraceDetailedFunctionCall, argument_data) +
210 : args_size;
211 :
212 E : if (!segment->CanAllocate(data_size) && !FlushSegment(segment))
213 i : return;
214 E : DCHECK(segment->CanAllocate(data_size));
215 :
216 : TraceDetailedFunctionCall* data =
217 E : segment->AllocateTraceRecord<TraceDetailedFunctionCall>(data_size);
218 E : data->function_id = function_id;
219 E : data->stack_trace_id = stack_trace_id;
220 E : data->argument_data_size = args_size;
221 :
222 E : if (serialize_timestamps_) {
223 E : base::AutoLock lock(lock_);
224 E : data->timestamp = call_counter_;
225 E : ++call_counter_;
226 E : } else {
227 E : data->timestamp = ::trace::common::GetTsc();
228 : }
229 :
230 E : if (args_size == 0)
231 i : return;
232 :
233 : // Output the number of arguments.
234 E : uint32_t* arg_sizes = reinterpret_cast<uint32_t*>(data->argument_data);
235 E : *(arg_sizes++) = args_count;
236 :
237 : // Output argument sizes.
238 E : if (arg_size0 > 0)
239 E : *(arg_sizes++) = arg_size0;
240 E : if (arg_size1 > 0)
241 i : *(arg_sizes++) = arg_size1;
242 E : if (arg_size2 > 0)
243 i : *(arg_sizes++) = arg_size2;
244 E : if (arg_size3 > 0)
245 i : *(arg_sizes++) = arg_size3;
246 E : if (arg_size4 > 0)
247 i : *(arg_sizes++) = arg_size4;
248 E : if (arg_size5 > 0)
249 i : *(arg_sizes++) = arg_size5;
250 :
251 : // Output argument data.
252 E : uint8_t* arg_data = reinterpret_cast<uint8_t*>(arg_sizes);
253 E : ArgumentSerializer<ArgType0>().serialize(arg0, arg_data);
254 E : arg_data += arg_size0;
255 E : ArgumentSerializer<ArgType1>().serialize(arg1, arg_data);
256 E : arg_data += arg_size1;
257 E : ArgumentSerializer<ArgType2>().serialize(arg2, arg_data);
258 E : arg_data += arg_size2;
259 E : ArgumentSerializer<ArgType3>().serialize(arg3, arg_data);
260 E : arg_data += arg_size3;
261 E : ArgumentSerializer<ArgType4>().serialize(arg4, arg_data);
262 E : arg_data += arg_size4;
263 E : ArgumentSerializer<ArgType5>().serialize(arg5, arg_data);
264 E : arg_data += arg_size5;
265 E : }
266 :
267 : // A templated helper for emitting a detailed function call record.
268 : // @param function_call_logger A pointer to the function call logger
269 : // instance to use.
270 : // @param segment A pointer to the TraceFileSegment to write to.
271 : // @param logger_serial A pointer to where the serial number of the previous
272 : // function call logger can be stored. Must be statically initialized to
273 : // -1.
274 : // @param function_id A pointer where the function ID can be saved. Must
275 : // be statically initialized to -1.
276 : // @param function_name The name of the function being traced.
277 : // @param arguments The arguments to the function being traced.
278 : template <typename... Arguments>
279 : inline void EmitDetailedFunctionCallHelper(
280 : FunctionCallLogger* function_call_logger,
281 : trace::client::TraceFileSegment* segment,
282 : uint32_t* logger_serial,
283 : uint32_t* function_id,
284 : const char* function_name,
285 E : const Arguments&... arguments) {
286 E : DCHECK_NE(static_cast<FunctionCallLogger*>(nullptr), function_call_logger);
287 E : DCHECK_NE(static_cast<trace::client::TraceFileSegment*>(nullptr), segment);
288 E : DCHECK_NE(static_cast<uint32_t*>(nullptr), logger_serial);
289 E : DCHECK_NE(static_cast<uint32_t*>(nullptr), function_id);
290 E : DCHECK_NE(static_cast<char*>(nullptr), function_name);
291 :
292 : // This is racy but safe because of the way GetFunctionId works and because
293 : // 32-bit writes are atomic.
294 E : if (*logger_serial != function_call_logger->serial() ||
295 : *function_id == -1) {
296 E : *logger_serial = function_call_logger->serial();
297 E : *function_id = function_call_logger->GetFunctionId(segment, function_name);
298 : }
299 E : uint32_t stack_trace_id = function_call_logger->GetStackTraceId(segment);
300 E : function_call_logger->EmitDetailedFunctionCall(
301 : segment, *function_id, stack_trace_id, arguments...);
302 E : }
303 :
304 : // A macro for emitting a detailed function call record. Automatically
305 : // emits a function name record the first time it is invoked for a given
306 : // function. This is a macro because it needs to use some function scope
307 : // static storage.
308 : // @param function_call_logger A pointer to the function call logger
309 : // instance to use.
310 : // @param segment A pointer to the TraceFileSegment to write to.
311 : #define EMIT_DETAILED_FUNCTION_CALL(function_call_logger, segment, ...) \
312 : { \
313 : static uint32_t logger_serial = UINT32_MAX; \
314 : static uint32_t function_id = UINT32_MAX; \
315 : EmitDetailedFunctionCallHelper((function_call_logger), (segment), \
316 : &logger_serial, &function_id, __FUNCTION__, \
317 : __VA_ARGS__); \
318 : }
319 :
320 : } // namespace memprof
321 : } // namespace agent
322 :
323 : #endif // SYZYGY_AGENT_MEMPROF_FUNCTION_CALL_LOGGER_H_
|