xref: /6.6.0/phosphor/include/phosphor/trace_log.h (revision 61e78e5c)
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 "trace_buffer.h"
30#include "trace_config.h"
31#include "trace_context.h"
32#include "trace_event.h"
33
34namespace phosphor {
35
36    /**
37     * The TraceLog class is the main public management interface
38     * of Phosphor. Generally the TraceLog is used as a singleton,
39     * this instance is acquired via TraceLog::getInstance().
40     *
41     * Logging can be enabled by passing in a TraceConfig object with
42     * the desired options to the TraceConfig::start() method:
43     *
44     *     // Enable tracing with a fixed buffer, 5 megabytes in size
45     *     TraceLog::getInstance().start(TraceConfig(BufferMode::fixed,
46     *                                               5 * 1024 * 1024))
47     *
48     * Once enabled, tracing will be logged wherever code has been
49     * instrumented with the instrumentation API described in phosphor.h.
50     *
51     * This class's public interface is *generally* thread-safe.
52     */
53    class PHOSPHOR_API TraceLog {
54    public:
55        /**
56         * Constructor for creating a TraceLog with a specific log
57         * config
58         *
59         * @param _config The TraceLogConfig to be used by the TraceLog
60         */
61        TraceLog(const TraceLogConfig& _config);
62
63        /**
64         * Constructor for generating a TraceLog from the current
65         * environment according to the rules set out in TraceLogConfig.
66         *
67         * Will additionally start tracing if the `PHOSPHOR_TRACING_START`
68         * environment variable is appropriately configured according to
69         * the rules for TraceConfig::fromString
70         *
71         * @return The configured TraceLog
72         */
73        TraceLog();
74
75        ~TraceLog();
76
77        /**
78         * Used to perform a one-time configuration of the TraceLog
79         *
80         * @param _config The TraceLogConfig to be used by the TraceLog
81         */
82        void configure(const TraceLogConfig& _config);
83
84        /**
85         * Singleton static method to get the TraceLog instance
86         *
87         * This is technically ThreadSafe according to the C++11 spec
88         * but versions of MSVC < 2015 are not *quite* conforming.
89         *
90         * @return the TraceLog instance
91         */
92        static TraceLog& getInstance();
93
94        /**
95         * Start tracing with the specified config
96         *
97         * @param _trace_config TraceConfig to use for this tracing run
98         */
99        void start(const TraceConfig& _trace_config);
100
101        /**
102         * Start tracing with the specified config and using an external lock
103         *
104         * @param lock guard holding the external lock
105         * @param _trace_config TraceConfig to use for this tracing run
106         */
107        void start(std::lock_guard<TraceLog>&,
108                   const TraceConfig& _trace_config);
109
110        /**
111         * Immediately stops tracing
112         */
113        void stop(bool shutdown = false);
114
115        /**
116         * Immediately stops tracing (With external locking)
117         *
118         * @param Lock guard holding the external lock
119         */
120        void stop(std::lock_guard<TraceLog>&, bool shutdown = false);
121
122        /**
123         * Logs an event in the current buffer (if applicable)
124         *
125         * This method should not be used directly, instead the
126         * macros contained within phosphor.h should be used instead.
127         *
128         * @param tpi Tracepoint info (name, category, etc) of the event.
129         * @param argA Argument to be saved with the event
130         * @param argB Argument to be saved with the event
131         */
132        void logEvent(const tracepoint_info* tpi,
133                      TraceArgument argA,
134                      TraceArgument argB);
135
136        /**
137         * Logs a Complete event in the current buffer (if applicable)
138         *
139         * This method should not be used directly, instead the
140         * macros contained within phosphor.h should be used instead.
141         *
142         * @param tpi Tracepoint information (name, category, ...)
143         * @param start Start time of the event
144         * @param duration Duration of the event
145         * @param argA Argument to be saved with the event
146         * @param argB Argument to be saved with the event
147         */
148        void logEvent(const tracepoint_info* tpi,
149                      std::chrono::steady_clock::time_point start,
150                      std::chrono::steady_clock::duration duration,
151                      TraceArgument argA,
152                      TraceArgument argB);
153
154        /**
155         * Used to get a reference to a reusable CategoryStatus. This should
156         * generally be held in a block-scope static at a given trace point
157         * to verify if the category for that trace point is presently
158         * enabled.
159         *
160         * @param category_group The category group to check
161         * @return const reference to the CategoryStatus atomic that holds
162         *         that status for the given category group
163         */
164        const AtomicCategoryStatus& getCategoryStatus(
165            const char* category_group);
166
167        /**
168         * Transfers ownership of the current TraceBuffer to the caller
169         *
170         * Should only be called while Tracing is disabled. The returned
171         * unique_ptr is not guaranteed to be non-null, and in fact *will*
172         * be null if the buffer has previously been retrieved.
173         *
174         * @return TraceBuffer
175         * @throw std::logic_error if tracing is currently enabled
176         */
177        std::unique_ptr<TraceBuffer> getBuffer();
178
179        /**
180         * Same as TraceLog::getBuffer() except with external locking
181         *
182         * @param Lock guard holding the external lock
183         * @return TraceBuffer
184         * @throw std::logic_error if tracing is currently enabled
185         */
186        std::unique_ptr<TraceBuffer> getBuffer(std::lock_guard<TraceLog>&);
187
188        /**
189         * Returns a trace context object which can be used to generate
190         * a JSON export / iterated over
191         *
192         * @return TraceContext for the last trace
193         * @throw std::logic_error if tracing is currently enabled
194         */
195        TraceContext getTraceContext();
196
197        /**
198         * Same as TraceLog::getTraceContext() except with external locking
199         *
200         * @return TraceContext for the last trace
201         * @throw std::logic_error if tracing is currently enabled
202         */
203        TraceContext getTraceContext(std::lock_guard<TraceLog>&);
204
205        /**
206         * Get the current state of tracing of this TraceLog
207         *
208         * @return True if tracing is enabled, False if tracing is disabled
209         */
210        bool isEnabled() const;
211
212        /**
213         * Registers the current thread for tracing (Optional)
214         *
215         * Can also give a name to associate the thread's TID with a name
216         * for export purposes. Thread name is ignored if it's an empty string.
217         *
218         * Registering a thread is used for a long-living thread and is
219         * used to give it a dedicated ChunkTenant and optionally a
220         * name. A registered thread MUST be de-registered before the
221         * thread is joined as the act of registering allocates resources
222         * only accessibly via thread -local storage.
223         *
224         * Registering is desirable as the thread will otherwise share
225         * a ChunkTenant with all other non-registered threads. Because
226         * this involves acquiring locks that may be under contention
227         * this can be expensive.
228         *
229         * @param thread_name (optional) name of the thread to register
230         * @throw std::logic_error if the thread has already been registered
231         */
232        void registerThread(const std::string& thread_name = "");
233
234        /**
235         * De-registers the current thread
236         *
237         * De-registering will free up any resources associated with the thread.
238         * This MUST be called from registered threads before they shutdown to
239         * prevent memory-leaks as the only reference to resources they allocate
240         * are in thread local storage.
241         */
242        void deregisterThread();
243
244        /**
245         * Lock the trace log externally
246         *
247         * Note: Prefer the internal locking on the methods
248         */
249        void lock() {
250            mutex.lock();
251        }
252
253        /**
254         * Unlock the trace log externally
255         *
256         * Note: Prefer the internal locking on the methods
257         */
258        void unlock() {
259            mutex.unlock();
260        }
261
262        TraceConfig getTraceConfig() const;
263
264        /**
265         * Invokes methods on the callback to supply various
266         * stats about the TraceLog, the active trace buffer
267         * and the category registry.
268         */
269        void getStats(StatsCallback& addStats) const;
270
271    protected:
272
273        /**
274         * Gets a pointer to the appropriate ChunkTenant (or nullptr)
275         * with the lock acquired.
276         *
277         * @return A valid ChunkTenant with available events or a
278         *         nullptr if a valid ChunkTenant could not be acquired.
279         */
280        std::unique_lock<ChunkTenant> getChunkTenant();
281
282        /**
283         * Replaces the current chunk held by the ChunkTenant with a new chunk
284         * (typically because it is full).
285         *
286         * This function must be called while the ChunkTenant lock is held.
287         *
288         * @param ct The ChunkTenant that should have it's chunk returned
289         *           and replaced
290         * @return true if the chunk has been successfully
291         *              replaced, false otherwise
292         */
293        bool replaceChunk(ChunkTenant& ct);
294
295        /**
296         * Used for evicting all ChunkTenants from the log
297         *
298         * This will use the set of ChunkTenants established from
299         * the calls to registerThread and deregisterThread and
300         * call close() on them.
301         *
302         * This will effectively send a message to all ChunkTenants
303         * that their current references to chunks in their possession
304         * are no longer valid.
305         *
306         * Once this function returns the buffer that the TraceLog had
307         * SHOULD be safe to be freed / iterated etc.
308         */
309        void evictThreads(std::lock_guard<TraceLog>& lh);
310
311        /**
312         * Removes all threads from the thread_name map that are part of the
313         * deregistered threads set.
314         */
315        void clearDeregisteredThreads();
316
317        /**
318         * Attempts to stop tracing without waiting for internal lock
319         */
320        void maybe_stop(size_t _generation);
321
322        /**
323         * The current or last-used TraceConfig of the TraceLog, this
324         * is only ever set by the start() method.
325         *
326         * This is a full copy of a TraceConfig rather than a reference
327         * to allow reuse / modification of a TraceConfig that was
328         * passed in by a user.
329         */
330        TraceConfig trace_config;
331
332        /**
333         * Whether or not tracing is currently enabled
334         *
335         * By transitioning this boolean to false no new trace events
336         * will be logged, but any threads currently in the process
337         * of tracing an event MAY finish tracing the event they're on.
338         */
339        std::atomic<bool> enabled;
340
341        /**
342         * mutex is the 'global' lock for the TraceLog and should be
343         * acquired when modifying the TraceLog itself or the current
344         * TraceBuffer.
345         *
346         * It is not required to be acquired when modifying a loaned out
347         * TraceChunk contained within a ChunkTenant, this is
348         * protected by the ChunkTenant's ChunkLock lock instead.
349         */
350        mutable std::mutex mutex;
351
352        /**
353         * buffer is the current buffer that is being used by the TraceLog.
354         *
355         * While logging is enabled the buffer will have various chunks of
356         * the buffer loaned out.
357         *
358         * This buffer may become NULL once tracing stops if a user asks for
359         * it as movement semantics are used and buffers are only created
360         * when tracing starts.
361         */
362        std::unique_ptr<TraceBuffer> buffer;
363
364        /**
365         * The current tracing generation. This is incremented every-time
366         * tracing is stopped and is passed into the TraceBuffer when it is
367         * constructed in order to 'uniquely' identify it.
368         */
369        std::atomic<size_t> generation;
370
371        /**
372         * The set of ChunkTenants that have been registered to this TraceLog.
373         */
374        std::unordered_set<ChunkTenant*> registered_chunk_tenants;
375
376        /**
377         * Category registry which manages the enabled / disabled categories
378         */
379        CategoryRegistry registry;
380
381        /**
382         * Map of registered thread TIDs to names
383         */
384        std::unordered_map<uint64_t, std::string> thread_names;
385
386        /**
387         * List of deregistered thread ids while tracing is running
388         */
389        std::set<uint64_t> deregistered_threads;
390    };
391}
392