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 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 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 function_id,
71 : uint32 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 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 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> 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> StackIdSet;
127 : StackIdSet emitted_stack_ids_; // Under lock_.
128 :
129 : // A unique serial number generated at construction time. For unittesting.
130 : uint32 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* 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* buffer) {
158 : return;
159 E : }
160 : };
161 :
162 : // Implementation off the detailed function call logger. Populates a
163 : // TraceDetailedFunctionCall buffer with variable length encodings of
164 : // the arguments. Arguments are serialized using the ArgumentSerializer
165 : // helper.
166 : template<typename ArgType0,
167 : typename ArgType1,
168 : typename ArgType2,
169 : typename ArgType3,
170 : typename ArgType4,
171 : typename ArgType5>
172 : void FunctionCallLogger::EmitDetailedFunctionCall(TraceFileSegment* segment,
173 : uint32 function_id,
174 : uint32 stack_trace_id,
175 : ArgType0 arg0,
176 : ArgType1 arg1,
177 : ArgType2 arg2,
178 : ArgType3 arg3,
179 : ArgType4 arg4,
180 E : ArgType5 arg5) {
181 E : DCHECK_NE(static_cast<TraceFileSegment*>(nullptr), segment);
182 E : size_t args_count = 0;
183 E : size_t args_size = 0;
184 :
185 E : size_t arg_size0 = ArgumentSerializer<ArgType0>().size();
186 E : args_count += arg_size0 > 0 ? 1 : 0;
187 E : args_size += arg_size0;
188 :
189 E : size_t arg_size1 = ArgumentSerializer<ArgType1>().size();
190 E : args_count += arg_size1 > 0 ? 1 : 0;
191 E : args_size += arg_size1;
192 :
193 E : size_t arg_size2 = ArgumentSerializer<ArgType2>().size();
194 E : args_count += arg_size2 > 0 ? 1 : 0;
195 E : args_size += arg_size2;
196 :
197 E : size_t arg_size3 = ArgumentSerializer<ArgType3>().size();
198 E : args_count += arg_size3 > 0 ? 1 : 0;
199 E : args_size += arg_size3;
200 :
201 E : size_t arg_size4 = ArgumentSerializer<ArgType4>().size();
202 E : args_count += arg_size4 > 0 ? 1 : 0;
203 E : args_size += arg_size4;
204 :
205 E : size_t arg_size5 = ArgumentSerializer<ArgType5>().size();
206 E : args_count += arg_size5 > 0 ? 1 : 0;
207 E : args_size += arg_size5;
208 :
209 E : if (args_size > 0)
210 E : args_size += (args_count + 1) * sizeof(uint32);
211 : size_t data_size = FIELD_OFFSET(TraceDetailedFunctionCall, argument_data) +
212 E : args_size;
213 :
214 E : if (!segment->CanAllocate(data_size) && !FlushSegment(segment))
215 i : return;
216 E : DCHECK(segment->CanAllocate(data_size));
217 :
218 : TraceDetailedFunctionCall* data =
219 E : segment->AllocateTraceRecord<TraceDetailedFunctionCall>(data_size);
220 E : data->function_id = function_id;
221 E : data->stack_trace_id = stack_trace_id;
222 E : data->argument_data_size = args_size;
223 :
224 E : if (serialize_timestamps_) {
225 E : base::AutoLock lock(lock_);
226 E : data->timestamp = call_counter_;
227 E : ++call_counter_;
228 E : } else {
229 E : data->timestamp = ::trace::common::GetTsc();
230 : }
231 :
232 E : if (args_size == 0)
233 i : return;
234 :
235 : // Output the number of arguments.
236 E : uint32* arg_sizes = reinterpret_cast<uint32*>(data->argument_data);
237 E : *(arg_sizes++) = args_count;
238 :
239 : // Output argument sizes.
240 E : if (arg_size0 > 0)
241 E : *(arg_sizes++) = arg_size0;
242 E : if (arg_size1 > 0)
243 i : *(arg_sizes++) = arg_size1;
244 E : if (arg_size2 > 0)
245 i : *(arg_sizes++) = arg_size2;
246 E : if (arg_size3 > 0)
247 i : *(arg_sizes++) = arg_size3;
248 E : if (arg_size4 > 0)
249 i : *(arg_sizes++) = arg_size4;
250 E : if (arg_size5 > 0)
251 i : *(arg_sizes++) = arg_size5;
252 :
253 : // Output argument data.
254 E : uint8* arg_data = reinterpret_cast<uint8*>(arg_sizes);
255 E : ArgumentSerializer<ArgType0>().serialize(arg0, arg_data);
256 E : arg_data += arg_size0;
257 E : ArgumentSerializer<ArgType1>().serialize(arg1, arg_data);
258 E : arg_data += arg_size1;
259 E : ArgumentSerializer<ArgType2>().serialize(arg2, arg_data);
260 E : arg_data += arg_size2;
261 E : ArgumentSerializer<ArgType3>().serialize(arg3, arg_data);
262 E : arg_data += arg_size3;
263 E : ArgumentSerializer<ArgType4>().serialize(arg4, arg_data);
264 E : arg_data += arg_size4;
265 E : ArgumentSerializer<ArgType5>().serialize(arg5, arg_data);
266 E : arg_data += arg_size5;
267 E : }
268 :
269 : // A templated helper for emitting a detailed function call record.
270 : // @param function_call_logger A pointer to the function call logger
271 : // instance to use.
272 : // @param segment A pointer to the TraceFileSegment to write to.
273 : // @param logger_serial A pointer to where the serial number of the previous
274 : // function call logger can be stored. Must be statically initialized to
275 : // -1.
276 : // @param function_id A pointer where the function ID can be saved. Must
277 : // be statically initialized to -1.
278 : // @param function_name The name of the function being traced.
279 : // @param arguments The arguments to the function being traced.
280 : template<typename... Arguments>
281 : inline void EmitDetailedFunctionCallHelper(
282 : FunctionCallLogger* function_call_logger,
283 : trace::client::TraceFileSegment* segment,
284 : uint32* logger_serial,
285 : uint32* function_id,
286 : const char* function_name,
287 E : const Arguments&... arguments) {
288 E : DCHECK_NE(static_cast<FunctionCallLogger*>(nullptr), function_call_logger);
289 E : DCHECK_NE(static_cast<trace::client::TraceFileSegment*>(nullptr), segment);
290 E : DCHECK_NE(static_cast<uint32*>(nullptr), logger_serial);
291 E : DCHECK_NE(static_cast<uint32*>(nullptr), function_id);
292 E : DCHECK_NE(static_cast<char*>(nullptr), function_name);
293 :
294 : // This is racy but safe because of the way GetFunctionId works and because
295 : // 32-bit writes are atomic.
296 : if (*logger_serial != function_call_logger->serial() ||
297 E : *function_id == -1) {
298 E : *logger_serial = function_call_logger->serial();
299 E : *function_id = function_call_logger->GetFunctionId(segment, function_name);
300 : }
301 : uint32 stack_trace_id =
302 E : function_call_logger->GetStackTraceId(segment);
303 : function_call_logger->EmitDetailedFunctionCall(
304 E : segment, *function_id, stack_trace_id, arguments...);
305 E : }
306 :
307 : // A macro for emitting a detailed function call record. Automatically
308 : // emits a function name record the first time it is invoked for a given
309 : // function. This is a macro because it needs to use some function scope
310 : // static storage.
311 : // @param function_call_logger A pointer to the function call logger
312 : // instance to use.
313 : // @param segment A pointer to the TraceFileSegment to write to.
314 : #define EMIT_DETAILED_FUNCTION_CALL(function_call_logger, segment, ...) \
315 : { \
316 : static uint32 logger_serial = UINT32_MAX; \
317 : static uint32 function_id = UINT32_MAX; \
318 : EmitDetailedFunctionCallHelper((function_call_logger), (segment), \
319 : &logger_serial, &function_id, __FUNCTION__, \
320 : __VA_ARGS__); \
321 : }
322 :
323 : } // namespace memprof
324 : } // namespace agent
325 :
326 : #endif // SYZYGY_AGENT_MEMPROF_FUNCTION_CALL_LOGGER_H_
|