GCC Code Coverage Report


Directory: ./
File: src/debug.cc
Date: 2025-05-17 13:07:10
Exec Total Coverage
Lines: 82 124 66.1%
Functions: 21 28 75.0%
Branches: 66 187 35.3%

Line Branch Exec Source
1 // Copyright (C) 2009, 2010 by Florent Lamiraux, Thomas Moulard, CNRS.
2 //
3
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // 1. Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 // DAMAGE.
27
28 #include "hpp/util/debug.hh"
29
30 #include <boost/filesystem.hpp> // Need C++ 17 to remove this.
31 #include <chrono>
32 #include <cstdlib>
33 #include <fstream>
34 #include <iomanip>
35 #include <iostream>
36 #include <sstream>
37
38 #include "config.h"
39 #include "hpp/util/indent.hh"
40
41 #ifndef HPP_LOGGINGDIR
42 #error "Please define HPP_LOGGINGDIR to the default logging prefix."
43 #endif //! HPP_LOGGINGDIR
44
45 // Include unistd.h if available, otherwise use the dummy getpid
46 // function.
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #else
50 static int getpid() { return 0; }
51 #endif // HAVE_UNISTD_H
52
53 namespace hpp {
54 namespace debug {
55
56 /// \brief Environment variable used to change the logging
57 /// directory.
58 static const char* ENV_LOGGINGDIR = "HPP_LOGGINGDIR";
59 static const char* ENV_LOGGINGLEVEL = "HPP_LOGGINGLEVEL";
60
61 static int verbosity = static_cast<int>(verbosityLevel::error);
62
63 static int benchmarkEnabled = false;
64
65 namespace {
66 3 HPP_UTIL_LOCAL void makeDirectory(const std::string& filename) {
67 using namespace boost::filesystem;
68
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 path pathname(filename);
69
70
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 std::string dirname = pathname.parent_path().string();
71
72
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 boost::filesystem::create_directories(dirname);
73 3 }
74
75 struct SetVerbosityLevelFromEnvVar {
76 5 SetVerbosityLevelFromEnvVar() {
77 5 const char* levelStr = getenv(ENV_LOGGINGLEVEL);
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (levelStr) {
79 try {
80 int level = std::stoi(levelStr);
81 if (level >= 0) {
82 setVerbosityLevel(level);
83 } else {
84 std::cerr << ENV_LOGGINGLEVEL << " env var should not be negative."
85 << std::endl;
86 }
87 } catch (std::invalid_argument& e) {
88 std::cerr << "Could not interpret " << ENV_LOGGINGLEVEL
89 << " env var: " << e.what() << std::endl;
90 } catch (std::out_of_range& e) {
91 std::cerr << "Could not interpret " << ENV_LOGGINGLEVEL
92 << " env var: " << e.what() << std::endl;
93 }
94 }
95 5 }
96 };
97 } // namespace
98
99 2 std::string getPrefix(const std::string& packageName) {
100 2 std::string loggingPrefix;
101 2 const char* env = getenv(ENV_LOGGINGDIR);
102
103
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (env)
104
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 loggingPrefix = env;
105 else {
106 loggingPrefix = HPP_LOGGINGDIR;
107 if (!packageName.empty()) {
108 loggingPrefix += '/';
109 loggingPrefix += packageName;
110 }
111 }
112
113 2 return loggingPrefix;
114 }
115
116 2 std::string getFilename(const std::string& filename,
117 const std::string& packageName) {
118 2 std::string res(getPrefix(packageName));
119
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 res += "/";
120
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 res += filename;
121
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 makeDirectory(res);
122 2 return res;
123 }
124
125 int getVerbosityLevel() { return verbosity; }
126
127 void setVerbosityLevel(int level) { verbosity = level; }
128
129 bool isBenchmarkEnabled() { return benchmarkEnabled; }
130
131 void enableBenchmark(bool enable) { benchmarkEnabled = enable; }
132
133 17 Output::Output() {}
134
135 34 Output::~Output() {}
136
137 223 std::ostream& Output::writePrefix(std::ostream& stream, const Channel& channel,
138 char const* file, int line, char const*) {
139 using std::chrono::system_clock;
140 223 system_clock::time_point now_point = system_clock::now();
141 223 const std::time_t now = system_clock::to_time_t(now_point);
142
1/2
✓ Branch 1 taken 223 times.
✗ Branch 2 not taken.
223 const auto millis{std::chrono::duration_cast<std::chrono::milliseconds>(
143 223 now_point.time_since_epoch())
144 223 .count() %
145 223 1000};
146
147 223 stream << std::put_time(std::localtime(&now), "[%F %T") << "." << std::setw(3)
148
4/8
✓ Branch 2 taken 223 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 223 times.
✗ Branch 6 not taken.
✓ Branch 11 taken 223 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 223 times.
✗ Branch 15 not taken.
223 << std::setfill('0') << millis << ']' << channel.label() << ':' << file
149
8/16
✓ Branch 1 taken 223 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 223 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 223 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 223 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 223 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 223 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 223 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 223 times.
✗ Branch 23 not taken.
223 << ':' << line << ": ";
150 223 return stream;
151 }
152
153 27 Channel::Channel(const char* label, const subscribers_t& subscribers)
154 27 : label_(label), subscribers_(subscribers) {}
155
156 54 Channel::~Channel() {}
157
158 223 const char* Channel::label() const { return label_; }
159
160 void Channel::write(char const* file, int line, char const* function,
161 const std::string& data) {
162 for (Output* o : subscribers_)
163 if (o) o->write(*this, file, line, function, data);
164 }
165
166 122 void Channel::write(char const* file, int line, char const* function,
167 const std::stringstream& data) {
168
2/2
✓ Branch 5 taken 222 times.
✓ Branch 6 taken 122 times.
344 for (Output* o : subscribers_)
169
2/4
✓ Branch 0 taken 222 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 222 times.
✗ Branch 4 not taken.
222 if (o) o->write(*this, file, line, function, data);
170 122 }
171
172 6 ConsoleOutput::ConsoleOutput() {}
173
174 12 ConsoleOutput::~ConsoleOutput() {}
175
176 void ConsoleOutput::write(const Channel& channel, char const* file, int line,
177 char const* function, const std::string& data) {
178 writePrefix(std::cerr, channel, file, line, function);
179 std::cerr << incindent << data << decindent << std::flush;
180 }
181
182 122 void ConsoleOutput::write(const Channel& channel, char const* file, int line,
183 char const* function, const std::stringstream& data) {
184 122 writePrefix(std::cerr, channel, file, line, function);
185 122 std::cerr << incindent << data.rdbuf() << decindent << std::flush;
186
1/2
✓ Branch 4 taken 122 times.
✗ Branch 5 not taken.
122 data.rdbuf()->pubseekpos(0);
187 122 }
188
189 namespace {
190 1 HPP_UTIL_LOCAL std::string makeLogFile(const JournalOutput& journalOutput) {
191
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 makeDirectory(journalOutput.getFilename());
192 1 return journalOutput.getFilename();
193 }
194 } // namespace
195
196 11 JournalOutput::JournalOutput(std::string filename)
197
2/4
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
11 : filename(filename), lastFunction(), stream() {}
198
199 22 JournalOutput::~JournalOutput() {}
200
201 // package name is set to ``hpp'' here so that
202 // the journal can be shared between all hpp packages.
203 // Splitting log into multiple files would make difficult
204 // to track packages interactions.
205 2 std::string JournalOutput::getFilename() const {
206
4/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
4 static const std::string packageName = "hpp";
207
208
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::stringstream name;
209
4/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
2 name << filename << '.' << getpid() << ".log";
210
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 return debug::getFilename(name.str(), packageName);
211 2 }
212
213 void JournalOutput::write(const Channel& channel, char const* file, int line,
214 char const* function, const std::string& data) {
215 if (!stream.is_open()) stream.open(makeLogFile(*this).c_str());
216
217 if (lastFunction != function) {
218 if (!lastFunction.empty()) {
219 writePrefix(stream, channel, file, line, function);
220 stream << "exiting " << lastFunction << iendl;
221 }
222
223 writePrefix(stream, channel, file, line, function);
224 stream << "entering " << function << iendl;
225 lastFunction = function;
226 }
227
228 writePrefix(stream, channel, file, line, function);
229 stream << incindent << data << decindent << std::flush;
230 }
231
232 100 void JournalOutput::write(const Channel& channel, char const* file, int line,
233 char const* function, const std::stringstream& data) {
234
4/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 99 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
100 if (!stream.is_open()) stream.open(makeLogFile(*this).c_str());
235
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 99 times.
100 if (lastFunction != function) {
236
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!lastFunction.empty()) {
237 writePrefix(stream, channel, file, line, function);
238 stream << "exiting " << lastFunction << iendl;
239 }
240
241 1 writePrefix(stream, channel, file, line, function);
242 1 stream << "entering " << function << iendl;
243 1 lastFunction = function;
244 }
245
246 100 writePrefix(stream, channel, file, line, function);
247 100 stream << incindent << data.rdbuf() << decindent << std::flush;
248
1/2
✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
100 data.rdbuf()->pubseekpos(0);
249 100 }
250
251 5 Logging::Logging()
252 5 : console(),
253
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 journal("journal"),
254
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 benchmarkJournal("benchmark"),
255
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 error("ERROR", {&journal, &console}),
256
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 warning("WARNING", {&journal, &console}),
257
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 notice("NOTICE", {&journal, &console}),
258
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 info("INFO", {&journal}),
259
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
15 benchmark("BENCHMARK", {&benchmarkJournal}) {}
260
261 5 Logging::~Logging() {}
262
263 } // end of namespace debug.
264
265 } // end of namespace hpp.
266
267 // Global variables definitions.
268 namespace hpp {
269 namespace debug {
270 HPP_UTIL_DLLAPI Logging logging;
271
272 HPP_UTIL_DLLAPI SetVerbosityLevelFromEnvVar setVerbosityLevelFromEnvVar;
273 } // end of namespace debug
274 } // end of namespace hpp
275