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 <memory>
21#include <mutex>
22
23#include "trace_buffer.h"
24
25namespace phosphor {
26
27    // Forward declare
28    class PHOSPHOR_API TraceLog;
29    class PHOSPHOR_API TraceConfig;
30
31    /**
32     * Functor type for a callback to be used when a TraceLog stops
33     * tracing. This can be given to a TraceConfig before tracing
34     * starts in order to detect when it ends.
35     *
36     * The functor will receive a reference to the TraceLog and a
37     * reference to the external lock that was held when the functor
38     * was invoked which can be used to access restricted methods
39     * even while the TraceLog is locked.
40     *
41     * Example Functor:
42     *
43     *     void(TraceLog& log, std::lock_guard<TraceLog>& lh) {
44     *         for(const auto& event : *log.getBuffer(lh)) {
45     *             std::cerr << event << "\n";
46     *         }
47     *         log.start(lh, TraceConfig(BufferMode::fixed, 2000000));
48     *     }
49     *
50     * It is worth bearing in mind that this callback will be run in
51     * the thread that stopped tracing which *could* be a thread which
52     * was in the middle of tracing an event if the buffer became full.
53     *
54     * Therefore it might be sensible to create a new thread to process
55     * the buffer (Which can take a comparatively long time) or to stash
56     * it somewhere that another thread can access.
57     */
58    class TracingStoppedCallback {
59    public:
60        virtual ~TracingStoppedCallback() {}
61
62        virtual void operator()(TraceLog&, std::lock_guard<TraceLog>&) = 0;
63
64    };
65
66    /**
67     * ostream operator overload for BufferMode
68     */
69    PHOSPHOR_API
70    std::ostream& operator<<(std::ostream& stream, const BufferMode mode);
71
72    /**
73     * Custom deleter for our StringPtr class. Exists to create an
74     * explicitly non-inline destructor.
75     */
76    struct PHOSPHOR_API StringPtrDeleter {
77        void operator()(const std::string* ptr);
78    };
79
80    /**
81     * String is used to expose string objects to clients, for example
82     * when reading phosphor config strings.
83     *
84     * This is part of the DLL interface, and hence we need a type
85     * which cannot be deleted directly by the client application -
86     * phosphor itself must delete it to prevent mismatches between
87     * allocate and free. Therefore we use a unique_ptr with a custom
88     * (non-inline) deleter to ensure phosphor itself always deletes
89     * any instances.
90     */
91    using StringPtr = std::unique_ptr<const std::string, StringPtrDeleter>;
92
93    /**
94     * make_unique equivilent for the StringPtr class.
95     *
96     * This should be used to contruct all instances of StringPtr, to ensure
97     * matching alloc/free.
98     */
99    StringPtr make_String(const std::string& str);
100
101    /**
102     * The TraceLogConfig is used to perform a one-time config of a
103     * TraceLog for anything that must be set only once e.g. the config
104     * to automatically use on startup.
105     *
106     * The TraceLogConfig can either be passed in when the TraceLog is
107     * created, or by using the TraceLog::configure() method *prior* to
108     * the first time the TraceLog is started.
109     */
110    class PHOSPHOR_API TraceLogConfig {
111    public:
112        /**
113         * Default constructor
114         */
115        TraceLogConfig() {
116        }
117
118        /**
119         * Sets the TraceLog to start tracing immediately on construction
120         * with a particular config
121         *
122         * @param _startup_trace A reference to a preexisting config that
123         *                       will be copied for internal storage.
124         * @return A reference to this config
125         */
126        TraceLogConfig& setStartupTrace(const TraceConfig& _startup_trace);
127
128        /**
129         * Clears the startup trace config that has previously been stored
130         * so that tracing on startup can be disabled.
131         *
132         * @return A reference to this config
133         */
134        TraceLogConfig& clearStartupTrace();
135
136        /**
137         * @return a pointer to the TraceConfig (Because it is potentially null,
138         *         i.e. tracing should not start)
139         */
140        TraceConfig* getStartupTrace() const;
141
142        /**
143         * Factory method which sets up a TraceLogConfig from the
144         * environment variables
145         *
146         * Note: This is a member function because MSVC2012 can't do
147         * copy elision properly.
148         *
149         * @param the TraceLogConfig to initialise
150         */
151        TraceLogConfig& fromEnvironment();
152
153    protected:
154        std::unique_ptr<TraceConfig> startup_trace;
155    };
156
157    /**
158     * The TraceConfig is used to configure a TraceLog for starting Trace
159     * when it is enabled.
160     *
161     * The TraceConfig has two constructors with two different aims in mind
162     *
163     *   - Using a built-in TraceBuffer type, either ring or fixed
164     *   - Using a user supplied TraceBuffer by supplying a TraceBufferFactory
165     *
166     * The first of these is specified by using the mode enumeration, the
167     * second is specified by passing in the TraceBufferFactory.
168     *
169     * The second parameter to both of these is the size in bytes of the
170     * TraceBuffer.
171     *
172     * All other arguments are optional and may be specified using chainable
173     * methods.
174     */
175    class PHOSPHOR_API TraceConfig {
176    public:
177        TraceConfig();
178
179        ~TraceConfig();
180
181        /**
182         * Constructor used when using a builtin TraceBuffer type
183         *
184         * @param _buffer_mode Which buffer mode to use. Cannot be
185         *                    BufferMode::Custom.
186         * @param _buffer_size Maximum size in bytes of the trace buffer.
187         */
188        TraceConfig(BufferMode _buffer_mode, size_t _buffer_size);
189
190        /**
191         * Constructor used when supplying a custom TraceBuffer implementation
192         *
193         * @param _buffer_factory The trace buffer factory to be used.
194         * @param _buffer_size Maximum size in bytes of the trace buffer.
195         */
196        TraceConfig(trace_buffer_factory _buffer_factory, size_t _buffer_size);
197
198        /**
199         * @return The buffer mode that is selected
200         */
201        BufferMode getBufferMode() const;
202
203        /**
204         * @return The size of the buffer in megabytes that will be used
205         */
206        size_t getBufferSize() const;
207
208        /**
209         * @return The trace buffer factory that will be used to create a
210         *         TraceBuffer when tracing is enabled.
211         */
212        trace_buffer_factory getBufferFactory() const;
213
214        /**
215         * Set the tracing_stopped_callback to be invoked when tracing
216         * stops.
217         *
218         * @param _tracing_stopped_callback Callback to be used. Note
219         *        this is passed as a shared_ptr due to it not being
220         *        safe to copy Callbacks in the general case, as it
221         *        may have been allocated with a different CRT than
222         *        phosphor itself is linked against.
223         * @return reference to the TraceConfig be configured
224         */
225        TraceConfig& setStoppedCallback(
226            std::shared_ptr<TracingStoppedCallback> _tracing_stopped_callback);
227
228        /**
229         * @return The tracing_stopped_callback to be invoked when tracing
230         * stops.
231         */
232        TracingStoppedCallback* getStoppedCallback() const;
233
234        /**
235         * Sets whether or not the tracing shutdown (and therefore callbacks)
236         * should be run when the TraceLog is destroyed. Defaults to false.
237         *
238         * @param _stop_tracing Stop tracing on shutdown
239         * @return reference to the TraceConfig being configured
240         */
241        TraceConfig& setStopTracingOnDestruct(bool _stop_tracing);
242
243        /**
244         * @return Whether or not the tracing shutdown (and therefore callbacks)
245         *         should be run when the TraceLog is destroyed.
246         */
247        bool getStopTracingOnDestruct() const;
248
249        /**
250         * Set the categories to enable/disable in this trace config
251         *
252         * @param enabled The categories to explicitly enable
253         * @param disabled The categories to explicitly disable
254         * @return reference to the TraceConfig being configured
255         */
256        TraceConfig& setCategories(const std::vector<std::string>& enabled,
257                                   const std::vector<std::string>& disabled);
258
259        /**
260         * @return The enabled categories for this trace config
261         */
262        const std::vector<std::string>& getEnabledCategories() const;
263
264        /**
265         * @return The disabled categories for this trace config
266         */
267        const std::vector<std::string>& getDisabledCategories() const;
268
269        /**
270         * Update a pre-existing TraceConfig from a config string
271         *
272         * Example:
273         *
274         *     config.updateFromString("buffer-mode:fixed,buffer-size:1024");
275         *
276         * @param config Config string to be used to update the TraceConfig
277         * @throws std::invalid_argument
278         */
279        void updateFromString(const std::string& config);
280
281        /**
282         * Generate a TraceConfig from a config string
283         *
284         * Example:
285         *
286         *     TraceConfig::fromString("buffer-mode:fixed,buffer-size:1024");
287         *
288         * @param config Config string to be used to generate the TraceConfig
289         * @return Generated TraceConfig
290         * @throws std::invalid_argument
291         */
292        static TraceConfig fromString(const std::string& config);
293
294        /**
295         * Converts a TraceConfig to a config string.
296         *
297         * This can be used to convert the TraceConfig that a TraceLog is
298         * using to a human readable form.
299         *
300         * @return The config string
301         */
302        StringPtr toString() const;
303
304    protected:
305        // Utility class to simplify maintenance of the mode/factory invariants
306        struct BufferFactoryContainer {
307            BufferFactoryContainer(BufferMode m);
308            BufferFactoryContainer(trace_buffer_factory f);
309
310            /**
311             * Get the trace buffer factory for the given mode.
312             *
313             * Cannot be used for the custom mode
314             *
315             * @param mode The trace buffer mode to convert
316             * @return The trace buffer factory for
317             * @throw std::invalid argument if given mode is invalid
318             */
319            static trace_buffer_factory modeToFactory(BufferMode mode);
320
321            BufferMode mode;
322            trace_buffer_factory factory;
323        };
324
325        BufferFactoryContainer buffer_factory_container{BufferMode::fixed};
326        size_t buffer_size = 0;
327
328        std::shared_ptr<TracingStoppedCallback> tracing_stopped_callback;
329        bool stop_tracing = false;
330
331        std::vector<std::string> enabled_categories;
332        std::vector<std::string> disabled_categories;
333    };
334
335}
336