xref: /5.5.2/phosphor/include/phosphor/trace_log.h (revision cd658760)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2016 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#pragma once
19
20#include <atomic>
21#include <memory>
22#include <mutex>
23#include <set>
24#include <unordered_map>
25#include <unordered_set>
26
27#include "category_registry.h"
28#include "chunk_lock.h"
29#include "platform/thread.h"
30#include "trace_buffer.h"
31#include "trace_config.h"
32#include "trace_context.h"
33#include "trace_event.h"
34
35namespace phosphor {
36
37    /**
38     * The TraceLog class is the main public management interface
39     * of Phosphor. Generally the TraceLog is used as a singleton,
40     * this instance is acquired via TraceLog::getInstance().
41     *
42     * Logging can be enabled by passing in a TraceConfig object with
43     * the desired options to the TraceConfig::start() method:
44     *
45     *     // Enable tracing with a fixed buffer, 5 megabytes in size
46     *     TraceLog::getInstance().start(TraceConfig(BufferMode::fixed,
47     *                                               5 * 1024 * 1024))
48     *
49     * Once enabled, tracing will be logged wherever code has been
50     * instrumented with the instrumentation API described in phosphor.h.
51     *
52     * This class's public interface is *generally* thread-safe.
53     */
54    class PHOSPHOR_API TraceLog {
55    public:
56        /**
57         * Constructor for creating a TraceLog with a specific log
58         * config
59         *
60         * @param _config The TraceLogConfig to be used by the TraceLog
61         */
62        TraceLog(const TraceLogConfig& _config);
63
64        /**
65         * Constructor for generating a TraceLog from the current
66         * environment according to the rules set out in TraceLogConfig.
67         *
68         * Will additionally start tracing if the `PHOSPHOR_TRACING_START`
69         * environment variable is appropriately configured according to
70         * the rules for TraceConfig::fromString
71         *
72         * @return The configured TraceLog
73         */
74        TraceLog();
75
76        ~TraceLog();
77
78        /**
79         * Used to perform a one-time configuration of the TraceLog
80         *
81         * @param _config The TraceLogConfig to be used by the TraceLog
82         */
83        void configure(const TraceLogConfig& _config);
84
85        /**
86         * Singleton static method to get the TraceLog instance
87         *
88         * This is technically ThreadSafe according to the C++11 spec
89         * but versions of MSVC < 2015 are not *quite* conforming.
90         *
91         * @return the TraceLog instance
92         */
93        static TraceLog& getInstance();
94
95        /**
96         * Start tracing with the specified config
97         *
98         * @param _trace_config TraceConfig to use for this tracing run
99         */
100        void start(const TraceConfig& _trace_config);
101
102        /**
103         * Start tracing with the specified config and using an external lock
104         *
105         * @param lock guard holding the external lock
106         * @param _trace_config TraceConfig to use for this tracing run
107         */
108        void start(std::lock_guard<TraceLog>&,
109                   const TraceConfig& _trace_config);
110
111        /**
112         * Immediately stops tracing
113         */
114        void stop(bool shutdown = false);
115
116        /**
117         * Immediately stops tracing (With external locking)
118         *
119         * @param Lock guard holding the external lock
120         */
121        void stop(std::lock_guard<TraceLog>&, bool shutdown = false);
122
123        /**
124         * Logs an event in the current buffer (if applicable)
125         *
126         * This method should not be used directly, instead the
127         * macros contained within phosphor.h should be used instead.
128         *
129         * @param category The category to log the event into. This (and
130         *        the name) should usually be a string literal as the
131         *        pointer should remain valid until the buffer is freed.
132         * @param name The name of the event
133         * @param type The type of the event
134         * @param argA Argument to be saved with the event
135         * @param argB Argument to be saved with the event
136         */
137        template <typename T, typename U>
138        void logEvent(const tracepoint_info* tpi,
139                      TraceEvent::Type type,
140                      T argA,
141                      U argB) {
142            if (!enabled) {
143                return;
144            }
145            auto cl = getChunkTenant();
146            if (cl) {
147                cl.mutex()->chunk->addEvent() = TraceEvent(
148                        tpi,
149                        type,
150                        platform::getCurrentThreadIDCached(),
151                        {{TraceArgumentConversion<T>::asArgument(argA),
152                          TraceArgumentConversion<U>::asArgument(argB)}},
153                        {{TraceArgumentConversion<T>::getType(),
154                          TraceArgumentConversion<U>::getType()}});
155            }
156        }
157
158        /**
159         * Logs an event in the current buffer (if applicable)
160         *
161         * This method should not be used directly, instead the
162         * macros contained within phosphor.h should be used instead.
163         *
164         * @param category The category to log the event into. This (and
165         *        the name) should usually be a string literal as the
166         *        pointer should remain valid until the buffer is freed.
167         * @param name The name of the event
168         * @param type The type of the event
169         * @param argA Argument to be saved with the event
170         */
171        template <typename T>
172        void logEvent(const tracepoint_info* tpi,
173                      TraceEvent::Type type,
174                      T argA) {
175            logEvent(tpi, type, argA, NoneType());
176        }
177
178        /**
179         * Logs an event in the current buffer (if applicable)
180         *
181         * This method should not be used directly, instead the
182         * macros contained within phosphor.h should be used instead.
183         *
184         * @param category The category to log the event into. This (and
185         *        the name) should usually be a string literal as the
186         *        pointer should remain valid until the buffer is freed.
187         * @param name The name of the event
188         * @param type The type of the event
189         */
190        void logEvent(const tracepoint_info* tpi,
191                      TraceEvent::Type type) {
192            logEvent(tpi, type, NoneType(), NoneType());
193        }
194
195        /**
196         * Logs a Complete event in the current buffer (if applicable)
197         *
198         * This method should not be used directly, instead the
199         * macros contained within phosphor.h should be used instead.
200         *
201         * @param tpi Tracepoint information (name, category, ...)
202         * @param start Start time of the event
203         * @param duration Duration of the event
204         * @param argA Argument to be saved with the event
205         * @param argB Argument to be saved with the event
206         */
207        template <typename T, typename U>
208        void logEvent(const tracepoint_info* tpi,
209                      std::chrono::steady_clock::time_point start,
210                      std::chrono::steady_clock::duration duration,
211                      T argA,
212                      U argB) {
213            if (!enabled) {
214                return;
215            }
216            auto cl = getChunkTenant();
217            if (cl) {
218                cl.mutex()->chunk->addEvent() = TraceEvent(
219                        tpi,
220                        platform::getCurrentThreadIDCached(),
221                        start,
222                        duration,
223                        {{TraceArgumentConversion<T>::asArgument(argA),
224                          TraceArgumentConversion<U>::asArgument(argB)}},
225                        {{TraceArgumentConversion<T>::getType(),
226                          TraceArgumentConversion<U>::getType()}});
227            }
228        }
229
230        /**
231         * Logs a Complete event in the current buffer (if applicable)
232         *
233         * This method should not be used directly, instead the
234         * macros contained within phosphor.h should be used instead.
235         *
236         * @param tpi Tracepoint information (name, category, ...)
237         * @param start Start time of the event
238         * @param duration Duration of the event
239         * @param argA Argument to be saved with the event
240         */
241        template <typename T>
242        void logEvent(const tracepoint_info* tpi,
243                      std::chrono::steady_clock::time_point start,
244                      std::chrono::steady_clock::duration duration,
245                      T argA) {
246            logEvent(tpi, start, duration, argA, NoneType());
247        }
248
249        /**
250         * Logs an event in the current buffer (if applicable)
251         *
252         * This method should not be used directly, instead the
253         * macros contained within phosphor.h should be used instead.
254         *
255         * @param category The category to log the event into. This (and
256         *        the name) should usually be a string literal as the
257         *        pointer should remain valid until the buffer is freed.
258         * @param name The name of the event
259         * @param type The type of the event
260         */
261        void logEvent(const tracepoint_info* tpi,
262                      std::chrono::steady_clock::time_point start,
263                      std::chrono::steady_clock::duration duration) {
264            logEvent(tpi, start, duration, NoneType(), NoneType());
265        }
266
267        /**
268         * Used to get a reference to a reusable CategoryStatus. This should
269         * generally be held in a block-scope static at a given trace point
270         * to verify if the category for that trace point is presently
271         * enabled.
272         *
273         * @param category_group The category group to check
274         * @return const reference to the CategoryStatus atomic that holds
275         *         that status for the given category group
276         */
277        const AtomicCategoryStatus& getCategoryStatus(
278            const char* category_group);
279
280        /**
281         * Transfers ownership of the current TraceBuffer to the caller
282         *
283         * Should only be called while Tracing is disabled. The returned
284         * unique_ptr is not guaranteed to be non-null, and in fact *will*
285         * be null if the buffer has previously been retrieved.
286         *
287         * @return TraceBuffer
288         * @throw std::logic_error if tracing is currently enabled
289         */
290        std::unique_ptr<TraceBuffer> getBuffer();
291
292        /**
293         * Same as TraceLog::getBuffer() except with external locking
294         *
295         * @param Lock guard holding the external lock
296         * @return TraceBuffer
297         * @throw std::logic_error if tracing is currently enabled
298         */
299        std::unique_ptr<TraceBuffer> getBuffer(std::lock_guard<TraceLog>&);
300
301        /**
302         * Returns a trace context object which can be used to generate
303         * a JSON export / iterated over
304         *
305         * @return TraceContext for the last trace
306         * @throw std::logic_error if tracing is currently enabled
307         */
308        TraceContext getTraceContext();
309
310        /**
311         * Same as TraceLog::getTraceContext() except with external locking
312         *
313         * @return TraceContext for the last trace
314         * @throw std::logic_error if tracing is currently enabled
315         */
316        TraceContext getTraceContext(std::lock_guard<TraceLog>&);
317
318        /**
319         * Get the current state of tracing of this TraceLog
320         *
321         * @return True if tracing is enabled, False if tracing is disabled
322         */
323        bool isEnabled() const;
324
325        /**
326         * Registers the current thread for tracing (Optional)
327         *
328         * Can also give a name to associate the thread's TID with a name
329         * for export purposes. Thread name is ignored if it's an empty string.
330         *
331         * Registering a thread is used for a long-living thread and is
332         * used to give it a dedicated ChunkTenant and optionally a
333         * name. A registered thread MUST be de-registered before the
334         * thread is joined as the act of registering allocates resources
335         * only accessibly via thread -local storage.
336         *
337         * Registering is desirable as the thread will otherwise share
338         * a ChunkTenant with all other non-registered threads. Because
339         * this involves acquiring locks that may be under contention
340         * this can be expensive.
341         *
342         * @param thread_name (optional) name of the thread to register
343         * @throw std::logic_error if the thread has already been registered
344         */
345        void registerThread(const std::string& thread_name = "");
346
347        /**
348         * De-registers the current thread
349         *
350         * De-registering will free up any resources associated with the thread.
351         * This MUST be called from registered threads before they shutdown to
352         * prevent memory-leaks as the only reference to resources they allocate
353         * are in thread local storage.
354         */
355        void deregisterThread();
356
357        /**
358         * Lock the trace log externally
359         *
360         * Note: Prefer the internal locking on the methods
361         */
362        void lock() {
363            mutex.lock();
364        }
365
366        /**
367         * Unlock the trace log externally
368         *
369         * Note: Prefer the internal locking on the methods
370         */
371        void unlock() {
372            mutex.unlock();
373        }
374
375        TraceConfig getTraceConfig() const;
376
377        /**
378         * Invokes methods on the callback to supply various
379         * stats about the TraceLog, the active trace buffer
380         * and the category registry.
381         */
382        void getStats(StatsCallback& addStats) const;
383
384    protected:
385
386        /**
387         * Gets a pointer to the appropriate ChunkTenant (or nullptr)
388         * with the lock acquired.
389         *
390         * @return A valid ChunkTenant with available events or a
391         *         nullptr if a valid ChunkTenant could not be acquired.
392         */
393        std::unique_lock<ChunkTenant> getChunkTenant();
394
395        /**
396         * Replaces the current chunk held by the ChunkTenant with a new chunk
397         * (typically because it is full).
398         *
399         * This function must be called while the ChunkTenant lock is held.
400         *
401         * @param ct The ChunkTenant that should have it's chunk returned
402         *           and replaced
403         * @return true if the chunk has been successfully
404         *              replaced, false otherwise
405         */
406        bool replaceChunk(ChunkTenant& ct);
407
408        /**
409         * Used for evicting all ChunkTenants from the log
410         *
411         * This will use the set of ChunkTenants established from
412         * the calls to registerThread and deregisterThread and
413         * call close() on them.
414         *
415         * This will effectively send a message to all ChunkTenants
416         * that their current references to chunks in their possession
417         * are no longer valid.
418         *
419         * Once this function returns the buffer that the TraceLog had
420         * SHOULD be safe to be freed / iterated etc.
421         */
422        void evictThreads(std::lock_guard<TraceLog>& lh);
423
424        /**
425         * Removes all threads from the thread_name map that are part of the
426         * deregistered threads set.
427         */
428        void clearDeregisteredThreads();
429
430        /**
431         * Attempts to stop tracing without waiting for internal lock
432         */
433        void maybe_stop(size_t _generation);
434
435        /**
436         * The current or last-used TraceConfig of the TraceLog, this
437         * is only ever set by the start() method.
438         *
439         * This is a full copy of a TraceConfig rather than a reference
440         * to allow reuse / modification of a TraceConfig that was
441         * passed in by a user.
442         */
443        TraceConfig trace_config;
444
445        /**
446         * Whether or not tracing is currently enabled
447         *
448         * By transitioning this boolean to false no new trace events
449         * will be logged, but any threads currently in the process
450         * of tracing an event MAY finish tracing the event they're on.
451         */
452        std::atomic<bool> enabled;
453
454        /**
455         * mutex is the 'global' lock for the TraceLog and should be
456         * acquired when modifying the TraceLog itself or the current
457         * TraceBuffer.
458         *
459         * It is not required to be acquired when modifying a loaned out
460         * TraceChunk contained within a ChunkTenant, this is
461         * protected by the ChunkTenant's ChunkLock lock instead.
462         */
463        mutable std::mutex mutex;
464
465        /**
466         * buffer is the current buffer that is being used by the TraceLog.
467         *
468         * While logging is enabled the buffer will have various chunks of
469         * the buffer loaned out.
470         *
471         * This buffer may become NULL once tracing stops if a user asks for
472         * it as movement semantics are used and buffers are only created
473         * when tracing starts.
474         */
475        std::unique_ptr<TraceBuffer> buffer;
476
477        /**
478         * The current tracing generation. This is incremented every-time
479         * tracing is stopped and is passed into the TraceBuffer when it is
480         * constructed in order to 'uniquely' identify it.
481         */
482        std::atomic<size_t> generation;
483
484        /**
485         * The set of ChunkTenants that have been registered to this TraceLog.
486         */
487        std::unordered_set<ChunkTenant*> registered_chunk_tenants;
488
489        /**
490         * Category registry which manages the enabled / disabled categories
491         */
492        CategoryRegistry registry;
493
494        /**
495         * Map of registered thread TIDs to names
496         */
497        std::unordered_map<uint64_t, std::string> thread_names;
498
499        /**
500         * List of deregistered thread ids while tracing is running
501         */
502        std::set<uint64_t> deregistered_threads;
503    };
504}
505