libfoedus-core
FOEDUS Core Library
debugging_supports.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
19 
20 #ifdef HAVE_PAPI
21 #include <papi.h>
22 #endif // HAVE_PAPI
23 
24 #include <glog/logging.h>
25 #include <glog/vlog_is_on.h>
26 #ifdef HAVE_GOOGLEPERFTOOLS
27 // #include <google/profiler.h> // older version of gperftools.
28 #include <gperftools/profiler.h>
29 #endif // HAVE_GOOGLEPERFTOOLS
30 
31 #include <cstring>
32 #include <mutex>
33 #include <string>
34 #include <vector>
35 
36 #include "foedus/engine.hpp"
39 #include "foedus/fs/filesystem.hpp"
40 
41 namespace foedus {
42 namespace debugging {
55 
62 
63 void DebuggingSupports::initialize_glog() {
64  std::lock_guard<std::mutex> guard(static_glog_initialize_lock); // implies fence too
65  ASSERT_ND(static_glog_initialize_counter >= 0);
66  if (static_glog_initialize_counter == 0) {
67  // Set the glog configurations.
68  const DebuggingOptions &options = engine_->get_options().debugging_;
69  FLAGS_logtostderr = options.debug_log_to_stderr_;
70  FLAGS_stderrthreshold = static_cast<int>(options.debug_log_stderr_threshold_);
71  FLAGS_minloglevel = static_cast<int>(options.debug_log_min_threshold_);
72  FLAGS_log_dir = options.debug_log_dir_.str(); // This one must be BEFORE InitGoogleLogging()
73  FLAGS_v = options.verbose_log_level_;
74  if (options.verbose_modules_.size() > 0) {
75  // Watch out for this bug, if we get a crash here:
76  // https://code.google.com/p/google-glog/issues/detail?id=172
77  google::SetVLOGLevel(options.verbose_modules_.str().c_str(), options.verbose_log_level_);
78  }
79 
80  fs::Path log_dir(options.debug_log_dir_.str());
81  if (!fs::exists(log_dir)) {
82  fs::create_directories(log_dir);
83  }
84 
85  // Use separate log files for the master engine and soc engine.
86  // If this is an emulated child SOC engine, anyway logs go to the master's log file.
87  const char* logfile_name;
88  if (engine_->is_master() || engine_->is_emulated_child()) {
89  logfile_name = "libfoedus";
90  } else {
91  // Note: Seems like InitGoogleLogging keeps the given argument without internally copying.
92  // if we give std::string.c_str() etc, this causes a problem.
93  // Thus, we need to give a really static const char*. mmm.
94  // Maybe we can have an array of size 256, defining const char* for each value? stupid..
95  logfile_name = "libfoedus_soc"; // std::to_string(engine_->get_soc_id());
96  }
97  google::InitGoogleLogging(logfile_name);
98  LOG(INFO) << "initialize_glog(): Initialized GLOG";
99  } else {
100  LOG(INFO) << "initialize_glog(): Observed that someone else has initialized GLOG";
101  }
103 }
104 
105 void DebuggingSupports::uninitialize_glog() {
106  std::lock_guard<std::mutex> guard(static_glog_initialize_lock); // implies fence too
107  ASSERT_ND(static_glog_initialize_counter >= 1);
108  if (static_glog_initialize_counter == 1) {
109  LOG(INFO) << "uninitialize_glog(): Uninitializing GLOG...";
110  google::ShutdownGoogleLogging();
111  } else {
112  LOG(INFO) << "uninitialize_glog(): There are still some other GLOG user.";
113  }
115 }
116 
118  // glog is already initialized by master, so emulated child does nothing
119  if (engine_->is_emulated_child()) {
120  return kRetOk;
121  }
122  initialize_glog(); // initialize glog at the beginning. we can use glog since now
123  std::memset(&papi_counters_, 0, sizeof(papi_counters_));
124  papi_enabled_ = false;
125  return kRetOk;
126 }
128  if (engine_->is_emulated_child()) {
129  return kRetOk;
130  }
131  uninitialize_glog(); // release glog at the end. we can't use glog since now
132  return kRetOk;
133 }
134 
136  FLAGS_logtostderr = value;
137  LOG(INFO) << "Changed glog's FLAGS_logtostderr to " << value;
138 }
140  FLAGS_stderrthreshold = static_cast<int>(level);
141  LOG(INFO) << "Changed glog's FLAGS_stderrthreshold to " << level;
142 }
144  FLAGS_minloglevel = static_cast<int>(level);
145  LOG(INFO) << "Changed glog's FLAGS_minloglevel to " << level;
146 }
148  FLAGS_v = verbose;
149  LOG(INFO) << "Changed glog's FLAGS_v to " << verbose;
150 }
151 void DebuggingSupports::set_verbose_module(const std::string &module, int verbose) {
152  // Watch out for this bug, if we get a crash here:
153  // https://code.google.com/p/google-glog/issues/detail?id=172
154  google::SetVLOGLevel(module.c_str(), verbose);
155  LOG(INFO) << "Invoked google::SetVLOGLevel for " << module << ", level=" << verbose;
156 }
157 
158 
159 #ifdef HAVE_PAPI
160 
161 #define X(a, b) a,
162 int kPapiEvents[] = {
163 #include "foedus/debugging/papi_events.xmacro" // NOLINT
164 };
165 #undef X
166 const uint16_t kPapiEventCount = sizeof(kPapiEvents) / sizeof(int);
167 
168 #define X_QUOTE(str) #str
169 #define X_EXPAND_AND_QUOTE(str) X_QUOTE(str)
170 #define X(a, b) X_EXPAND_AND_QUOTE(a) ": " b,
171 const char* kPapiEventNames[] = {
172 #include "foedus/debugging/papi_events.xmacro" // NOLINT
173 };
174 #undef X
175 #undef X_EXPAND_AND_QUOTE
176 #undef X_QUOTE
177 
179  papi_enabled_ = false;
180  int version = ::PAPI_library_init(PAPI_VER_CURRENT);
181  int total_counters = ::PAPI_num_counters();
182  if (total_counters <= PAPI_OK) {
183  LOG(ERROR) << "PAPI is not supported in this environment. PAPI runtime version=" << version
184  << ", PAPI_VER_CURRENT=" << PAPI_VER_CURRENT;
185  ::PAPI_shutdown();
186  return;
187  }
188  LOG(INFO) << "PAPI has " << total_counters << " counters. PAPI runtime version=" << version
189  << ", PAPI_VER_CURRENT=" << PAPI_VER_CURRENT;
190  int ret = ::PAPI_start_counters(kPapiEvents, kPapiEventCount);
191  if (ret != PAPI_OK) {
192  LOG(ERROR) << "PAPI_start_counters failed. retval=" << ret;
193  ::PAPI_shutdown();
194  } else {
195  LOG(INFO) << "Started counting " << kPapiEventCount << " performance events via PAPI";
196  papi_enabled_ = true;
197  }
198 }
200  if (papi_enabled_) {
201  int ret = ::PAPI_stop_counters(papi_counters_.counters_, kPapiEventCount);
202  if (ret != PAPI_OK) {
203  LOG(ERROR) << "PAPI_stop_counters failed. retval=" << ret;
204  }
205  ::PAPI_shutdown();
206  }
207 }
208 std::vector<std::string> DebuggingSupports::describe_papi_counters(const PapiCounters& counters) {
209  std::vector<std::string> ret;
210  for (uint16_t i = 0; i < kPapiEventCount; ++i) {
211  ret.emplace_back(std::string(kPapiEventNames[i]) + ":" + std::to_string(counters.counters_[i]));
212  }
213  return ret;
214 }
215 #else // HAVE_PAPI
217  LOG(WARNING) << "libpapi was not linked. No PAPI profile is collected.";
218 }
221  const PapiCounters& /*counters*/) {
222  std::vector<std::string> ret;
223  ret.emplace_back("libpapi was not linked. No PAPI profile is collected");
224  return ret;
225 }
226 #endif // HAVE_PAPI
227 
228 
229 ErrorStack DebuggingSupports::start_profile(const std::string& output_file) {
230 #ifdef HAVE_GOOGLEPERFTOOLS
231  int ret = ::ProfilerStart(output_file.c_str());
232  if (ret == 0) {
233  LOG(ERROR) << "ProfilerStart() returned zero (an error). os_error=" << assorted::os_error();
235  }
236 #else // HAVE_GOOGLEPERFTOOLS
237  LOG(WARNING) << "Google perftools was not linked. No profile is provided. " << output_file;
238 #endif // HAVE_GOOGLEPERFTOOLS
239  return kRetOk;
240 }
241 
243 #ifdef HAVE_GOOGLEPERFTOOLS
244  ::ProfilerStop();
245 #endif // HAVE_GOOGLEPERFTOOLS
246 }
247 
248 } // namespace debugging
249 } // namespace foedus
void set_debug_log_to_stderr(bool value)
Whether to write debug logs to stderr rather than log file.
void start_papi_counters()
Start collecting performance counters via PAPI if it's available.
long long int counters_[128]
wanna use int64_t, but to align with PAPI...
ErrorStack start_profile(const std::string &output_file)
Start running a CPU profiler (gperftools/PAPI).
bool debug_log_to_stderr_
Whether to write debug logs to stderr rather than log file.
#define ERROR_STACK(e)
Instantiates ErrorStack with the given foedus::error_code, creating an error stack with the current f...
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
assorted::FixedString< 252 > verbose_modules_
Per-module verbose level.
debugging::DebuggingOptions debugging_
Brings error stacktrace information as return value of functions.
Definition: error_stack.hpp:81
void set_debug_log_min_threshold(DebuggingOptions::DebugLogLevel level)
Debug logs below this level will be completely ignored.
const EngineOptions & get_options() const
Definition: engine.cpp:39
bool is_master() const
Returns if this engine object is a master instance.
Definition: engine.cpp:68
static std::vector< std::string > describe_papi_counters(const PapiCounters &counters)
Returns a human-readable explanation of PAPI counters.
void set_debug_log_stderr_threshold(DebuggingOptions::DebugLogLevel level)
Debug logs at or above this level will be copied to stderr.
Analogue of boost::filesystem::path.
Definition: path.hpp:37
bool is_emulated_child() const
Returns if this engine object is a child instance running just as a thread.
Definition: engine.cpp:69
int static_glog_initialize_counter
This and static_glog_initialize_lock are the only static variables we have in the entire code base...
void stop_profile()
Stop CPU profiling.
std::basic_string< CHAR > str() const
Convert to a std::string object.
bool create_directories(const Path &p, bool sync=false)
Recursive mkdir (mkdirs).
Definition: filesystem.cpp:89
bool exists(const Path &p)
Returns if the file exists.
Definition: filesystem.hpp:128
void set_verbose_module(const std::string &module, int verbose)
Per-module verbose level.
fs::FixedPath debug_log_dir_
Path of the folder to write debug logs.
std::mutex static_glog_initialize_lock
Exclusive lock variable for Google-logging's initialization/uninitialization.
std::string os_error()
Thread-safe strerror(errno).
void stop_papi_counters()
Stop collecting performance counters via PAPI.
DebugLogLevel debug_log_min_threshold_
Debug logs below this level will be completely ignored.
DebugLogLevel
Defines debug logging levels.
void set_verbose_log_level(int verbose)
Verbose debug logs (VLOG(m)) at or less than this number will be shown.
const ErrorStack kRetOk
Normal return value for no-error case.
Atomic fence methods and load/store with fences that work for both C++11/non-C++11 code...
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
uint32_t size() const noexcept
Returns the length of this string.
Set of options for debugging support.
0x0B01 : "DEBUG : Gperftools reported an error" .
Definition: error_code.hpp:212
DebugLogLevel debug_log_stderr_threshold_
Debug logs at or above this level will be copied to stderr.
int16_t verbose_log_level_
Verbose debug logs (VLOG(m)) at or less than this number will be shown.