xref: /6.0.3/phosphor/include/phosphor/trace_log.h (revision 6cfacecc)
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         * Used to get a reference to a reusable CategoryStatus. This should
197         * generally be held in a block-scope static at a given trace point
198         * to verify if the category for that trace point is presently
199         * enabled.
200         *
201         * @param category_group The category group to check
202         * @return const reference to the CategoryStatus atomic that holds
203         *         that status for the given category group
204         */
205        const AtomicCategoryStatus& getCategoryStatus(
206            const char* category_group);
207
208        /**
209         * Transfers ownership of the current TraceBuffer to the caller
210         *
211         * Should only be called while Tracing is disabled. The returned
212         * unique_ptr is not guaranteed to be non-null, and in fact *will*
213         * be null if the buffer has previously been retrieved.
214         *
215         * @return TraceBuffer
216         * @throw std::logic_error if tracing is currently enabled
217         */
218        std::unique_ptr<TraceBuffer> getBuffer();
219
220        /**
221         * Same as TraceLog::getBuffer() except with external locking
222         *
223         * @param Lock guard holding the external lock
224         * @return TraceBuffer
225         * @throw std::logic_error if tracing is currently enabled
226         */
227        std::unique_ptr<TraceBuffer> getBuffer(std::lock_guard<TraceLog>&);
228
229        /**
230         * Returns a trace context object which can be used to generate
231         * a JSON export / iterated over
232         *
233         * @return TraceContext for the last trace
234         * @throw std::logic_error if tracing is currently enabled
235         */
236        TraceContext getTraceContext();
237
238        /**
239         * Same as TraceLog::getTraceContext() except with external locking
240         *
241         * @return TraceContext for the last trace
242         * @throw std::logic_error if tracing is currently enabled
243         */
244        TraceContext getTraceContext(std::lock_guard<TraceLog>&);
245
246        /**
247         * Get the current state of tracing of this TraceLog
248         *
249         * @return True if tracing is enabled, False if tracing is disabled
250         */
251        bool isEnabled() const;
252
253        /**
254         * Registers the current thread for tracing (Optional)
255         *
256         * Can also give a name to associate the thread's TID with a name
257         * for export purposes. Thread name is ignored if it's an empty string.
258         *
259         * Registering a thread is used for a long-living thread and is
260         * used to give it a dedicated ChunkTenant and optionally a
261         * name. A registered thread MUST be de-registered before the
262         * thread is joined as the act of registering allocates resources
263         * only accessibly via thread -local storage.
264         *
265         * Registering is desirable as the thread will otherwise share
266         * a ChunkTenant with all other non-registered threads. Because
267         * this involves acquiring locks that may be under contention
268         * this can be expensive.
269         *
270         * @param thread_name (optional) name of the thread to register
271         * @throw std::logic_error if the thread has already been registered
272         */
273        void registerThread(const std::string& thread_name = "");
274
275        /**
276         * De-registers the current thread
277         *
278         * De-registering will free up any resources associated with the thread.
279         * This MUST be called from registered threads before they shutdown to
280         * prevent memory-leaks as the only reference to resources they allocate
281         * are in thread local storage.
282         */
283        void deregisterThread();
284
285        /**
286         * Lock the trace log externally
287         *
288         * Note: Prefer the internal locking on the methods
289         */
290        void lock() {
291            mutex.lock();
292        }
293
294        /**
295         * Unlock the trace log externally
296         *
297         * Note: Prefer the internal locking on the methods
298         */
299        void unlock() {
300            mutex.unlock();
301        }
302
303        TraceConfig getTraceConfig() const;
304
305        /**
306         * Invokes methods on the callback to supply various
307         * stats about the TraceLog, the active trace buffer
308         * and the category registry.
309         */
310        void getStats(StatsCallback& addStats) const;
311
312    protected:
313
314        /**
315         * Gets a pointer to the appropriate ChunkTenant (or nullptr)
316         * with the lock acquired.
317         *
318         * @return A valid ChunkTenant with available events or a
319         *         nullptr if a valid ChunkTenant could not be acquired.
320         */
321        std::unique_lock<ChunkTenant> getChunkTenant();
322
323        /**
324         * Replaces the current chunk held by the ChunkTenant with a new chunk
325         * (typically because it is full).
326         *
327         * This function must be called while the ChunkTenant lock is held.
328         *
329         * @param ct The ChunkTenant that should have it's chunk returned
330         *           and replaced
331         * @return true if the chunk has been successfully
332         *              replaced, false otherwise
333         */
334        bool replaceChunk(ChunkTenant& ct);
335
336        /**
337         * Used for evicting all ChunkTenants from the log
338         *
339         * This will use the set of ChunkTenants established from
340         * the calls to registerThread and deregisterThread and
341         * call close() on them.
342         *
343         * This will effectively send a message to all ChunkTenants
344         * that their current references to chunks in their possession
345         * are no longer valid.
346         *
347         * Once this function returns the buffer that the TraceLog had
348         * SHOULD be safe to be freed / iterated etc.
349         */
350        void evictThreads(std::lock_guard<TraceLog>& lh);
351
352        /**
353         * Removes all threads from the thread_name map that are part of the
354         * deregistered threads set.
355         */
356        void clearDeregisteredThreads();
357
358        /**
359         * Attempts to stop tracing without waiting for internal lock
360         */
361        void maybe_stop(size_t _generation);
362
363        /**
364         * The current or last-used TraceConfig of the TraceLog, this
365         * is only ever set by the start() method.
366         *
367         * This is a full copy of a TraceConfig rather than a reference
368         * to allow reuse / modification of a TraceConfig that was
369         * passed in by a user.
370         */
371        TraceConfig trace_config;
372
373        /**
374         * Whether or not tracing is currently enabled
375         *
376         * By transitioning this boolean to false no new trace events
377         * will be logged, but any threads currently in the process
378         * of tracing an event MAY finish tracing the event they're on.
379         */
380        std::atomic<bool> enabled;
381
382        /**
383         * mutex is the 'global' lock for the TraceLog and should be
384         * acquired when modifying the TraceLog itself or the current
385         * TraceBuffer.
386         *
387         * It is not required to be acquired when modifying a loaned out
388         * TraceChunk contained within a ChunkTenant, this is
389         * protected by the ChunkTenant's ChunkLock lock instead.
390         */
391        mutable std::mutex mutex;
392
393        /**
394         * buffer is the current buffer that is being used by the TraceLog.
395         *
396         * While logging is enabled the buffer will have various chunks of
397         * the buffer loaned out.
398         *
399         * This buffer may become NULL once tracing stops if a user asks for
400         * it as movement semantics are used and buffers are only created
401         * when tracing starts.
402         */
403        std::unique_ptr<TraceBuffer> buffer;
404
405        /**
406         * The current tracing generation. This is incremented every-time
407         * tracing is stopped and is passed into the TraceBuffer when it is
408         * constructed in order to 'uniquely' identify it.
409         */
410        std::atomic<size_t> generation;
411
412        /**
413         * The set of ChunkTenants that have been registered to this TraceLog.
414         */
415        std::unordered_set<ChunkTenant*> registered_chunk_tenants;
416
417        /**
418         * Category registry which manages the enabled / disabled categories
419         */
420        CategoryRegistry registry;
421
422        /**
423         * Map of registered thread TIDs to names
424         */
425        std::unordered_map<uint64_t, std::string> thread_names;
426
427        /**
428         * List of deregistered thread ids while tracing is running
429         */
430        std::set<uint64_t> deregistered_threads;
431    };
432}
433