rippled
PerfLogImp.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2018 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/basics/BasicConfig.h>
21 #include <ripple/basics/impl/PerfLogImp.h>
22 #include <ripple/beast/core/CurrentThreadName.h>
23 #include <ripple/beast/utility/Journal.h>
24 #include <ripple/core/JobTypes.h>
25 #include <ripple/json/json_writer.h>
26 #include <ripple/json/to_string.h>
27 #include <atomic>
28 #include <cstdint>
29 #include <cstdlib>
30 #include <iostream>
31 #include <iterator>
32 #include <mutex>
33 #include <optional>
34 #include <sstream>
35 #include <stdexcept>
36 #include <string>
37 #include <unordered_map>
38 #include <utility>
39 
40 namespace ripple {
41 namespace perf {
42 
44  std::vector<char const*> const& labels,
45  JobTypes const& jobTypes)
46 {
47  {
48  // populateRpc
49  rpc_.reserve(labels.size());
50  for (std::string const label : labels)
51  {
52  auto const inserted = rpc_.emplace(label, Rpc()).second;
53  if (!inserted)
54  {
55  // Ensure that no other function populates this entry.
56  assert(false);
57  }
58  }
59  }
60  {
61  // populateJq
62  jq_.reserve(jobTypes.size());
63  for (auto const& [jobType, _] : jobTypes)
64  {
65  auto const inserted = jq_.emplace(jobType, Jq()).second;
66  if (!inserted)
67  {
68  // Ensure that no other function populates this entry.
69  assert(false);
70  }
71  }
72  }
73 }
74 
77 {
79  // totalRpc represents all rpc methods. All that started, finished, etc.
80  Rpc totalRpc;
81  for (auto const& proc : rpc_)
82  {
83  Rpc value;
84  {
85  std::lock_guard lock(proc.second.mutex);
86  if (!proc.second.value.started && !proc.second.value.finished &&
87  !proc.second.value.errored)
88  {
89  continue;
90  }
91  value = proc.second.value;
92  }
93 
95  p[jss::started] = std::to_string(value.started);
96  totalRpc.started += value.started;
97  p[jss::finished] = std::to_string(value.finished);
98  totalRpc.finished += value.finished;
99  p[jss::errored] = std::to_string(value.errored);
100  totalRpc.errored += value.errored;
101  p[jss::duration_us] = std::to_string(value.duration.count());
102  totalRpc.duration += value.duration;
103  rpcobj[proc.first] = p;
104  }
105 
106  if (totalRpc.started)
107  {
108  Json::Value totalRpcJson(Json::objectValue);
109  totalRpcJson[jss::started] = std::to_string(totalRpc.started);
110  totalRpcJson[jss::finished] = std::to_string(totalRpc.finished);
111  totalRpcJson[jss::errored] = std::to_string(totalRpc.errored);
112  totalRpcJson[jss::duration_us] =
113  std::to_string(totalRpc.duration.count());
114  rpcobj[jss::total] = totalRpcJson;
115  }
116 
118  // totalJq represents all jobs. All enqueued, started, finished, etc.
119  Jq totalJq;
120  for (auto const& proc : jq_)
121  {
122  Jq value;
123  {
124  std::lock_guard lock(proc.second.mutex);
125  if (!proc.second.value.queued && !proc.second.value.started &&
126  !proc.second.value.finished)
127  {
128  continue;
129  }
130  value = proc.second.value;
131  }
132 
134  j[jss::queued] = std::to_string(value.queued);
135  totalJq.queued += value.queued;
136  j[jss::started] = std::to_string(value.started);
137  totalJq.started += value.started;
138  j[jss::finished] = std::to_string(value.finished);
139  totalJq.finished += value.finished;
140  j[jss::queued_duration_us] =
142  totalJq.queuedDuration += value.queuedDuration;
143  j[jss::running_duration_us] =
145  totalJq.runningDuration += value.runningDuration;
146  jqobj[JobTypes::name(proc.first)] = j;
147  }
148 
149  if (totalJq.queued)
150  {
151  Json::Value totalJqJson(Json::objectValue);
152  totalJqJson[jss::queued] = std::to_string(totalJq.queued);
153  totalJqJson[jss::started] = std::to_string(totalJq.started);
154  totalJqJson[jss::finished] = std::to_string(totalJq.finished);
155  totalJqJson[jss::queued_duration_us] =
157  totalJqJson[jss::running_duration_us] =
159  jqobj[jss::total] = totalJqJson;
160  }
161 
162  Json::Value counters(Json::objectValue);
163  // Be kind to reporting tools and let them expect rpc and jq objects
164  // even if empty.
165  counters[jss::rpc] = rpcobj;
166  counters[jss::job_queue] = jqobj;
167  return counters;
168 }
169 
172 {
173  auto const present = steady_clock::now();
174 
175  Json::Value jobsArray(Json::arrayValue);
176  auto const jobs = [this] {
177  std::lock_guard lock(jobsMutex_);
178  return jobs_;
179  }();
180 
181  for (auto const& j : jobs)
182  {
183  if (j.first == jtINVALID)
184  continue;
186  jobj[jss::job] = JobTypes::name(j.first);
187  jobj[jss::duration_us] = std::to_string(
188  std::chrono::duration_cast<microseconds>(present - j.second)
189  .count());
190  jobsArray.append(jobj);
191  }
192 
193  Json::Value methodsArray(Json::arrayValue);
194  std::vector<MethodStart> methods;
195  {
196  std::lock_guard lock(methodsMutex_);
197  methods.reserve(methods_.size());
198  for (auto const& m : methods_)
199  methods.push_back(m.second);
200  }
201  for (auto m : methods)
202  {
203  Json::Value methodobj(Json::objectValue);
204  methodobj[jss::method] = m.first;
205  methodobj[jss::duration_us] = std::to_string(
206  std::chrono::duration_cast<microseconds>(present - m.second)
207  .count());
208  methodsArray.append(methodobj);
209  }
210 
212  current[jss::jobs] = jobsArray;
213  current[jss::methods] = methodsArray;
214  return current;
215 }
216 
217 //-----------------------------------------------------------------------------
218 
219 void
221 {
222  if (setup_.perfLog.empty())
223  return;
224 
225  if (logFile_.is_open())
226  logFile_.close();
227 
228  auto logDir = setup_.perfLog.parent_path();
229  if (!boost::filesystem::is_directory(logDir))
230  {
231  boost::system::error_code ec;
232  boost::filesystem::create_directories(logDir, ec);
233  if (ec)
234  {
235  JLOG(j_.fatal()) << "Unable to create performance log "
236  "directory "
237  << logDir << ": " << ec.message();
238  signalStop_();
239  return;
240  }
241  }
242 
243  logFile_.open(setup_.perfLog.c_str(), std::ios::out | std::ios::app);
244 
245  if (!logFile_)
246  {
247  JLOG(j_.fatal()) << "Unable to open performance log " << setup_.perfLog
248  << ".";
249  signalStop_();
250  }
251 }
252 
253 void
255 {
256  beast::setCurrentThreadName("perflog");
258 
259  while (true)
260  {
261  {
263  if (cond_.wait_until(
264  lock, lastLog_ + setup_.logInterval, [&] { return stop_; }))
265  {
266  return;
267  }
268  if (rotate_)
269  {
270  openLog();
271  rotate_ = false;
272  }
273  }
274  report();
275  }
276 }
277 
278 void
280 {
281  if (!logFile_)
282  // If logFile_ is not writable do no further work.
283  return;
284 
285  auto const present = system_clock::now();
286  if (present < lastLog_ + setup_.logInterval)
287  return;
288  lastLog_ = present;
289 
291  report[jss::time] = to_string(std::chrono::floor<microseconds>(present));
292  {
294  report[jss::workers] =
295  static_cast<unsigned int>(counters_.jobs_.size());
296  }
297  report[jss::hostid] = hostname_;
298  report[jss::counters] = counters_.countersJson();
299  report[jss::current_activities] = counters_.currentJson();
300 
301  logFile_ << Json::Compact{std::move(report)} << std::endl;
302 }
303 
305  Setup const& setup,
306  beast::Journal journal,
307  std::function<void()>&& signalStop)
308  : setup_(setup), j_(journal), signalStop_(std::move(signalStop))
309 {
310  openLog();
311 }
312 
314 {
315  stop();
316 }
317 
318 void
319 PerfLogImp::rpcStart(std::string const& method, std::uint64_t const requestId)
320 {
321  auto counter = counters_.rpc_.find(method);
322  if (counter == counters_.rpc_.end())
323  {
324  assert(false);
325  return;
326  }
327 
328  {
329  std::lock_guard lock(counter->second.mutex);
330  ++counter->second.value.started;
331  }
333  counters_.methods_[requestId] = {
334  counter->first.c_str(), steady_clock::now()};
335 }
336 
337 void
339  std::string const& method,
340  std::uint64_t const requestId,
341  bool finish)
342 {
343  auto counter = counters_.rpc_.find(method);
344  if (counter == counters_.rpc_.end())
345  {
346  assert(false);
347  return;
348  }
349  steady_time_point startTime;
350  {
352  auto const e = counters_.methods_.find(requestId);
353  if (e != counters_.methods_.end())
354  {
355  startTime = e->second.second;
356  counters_.methods_.erase(e);
357  }
358  else
359  {
360  assert(false);
361  }
362  }
363  std::lock_guard lock(counter->second.mutex);
364  if (finish)
365  ++counter->second.value.finished;
366  else
367  ++counter->second.value.errored;
368  counter->second.value.duration += std::chrono::duration_cast<microseconds>(
369  steady_clock::now() - startTime);
370 }
371 
372 void
374 {
375  auto counter = counters_.jq_.find(type);
376  if (counter == counters_.jq_.end())
377  {
378  assert(false);
379  return;
380  }
381  std::lock_guard lock(counter->second.mutex);
382  ++counter->second.value.queued;
383 }
384 
385 void
387  JobType const type,
388  microseconds dur,
389  steady_time_point startTime,
390  int instance)
391 {
392  auto counter = counters_.jq_.find(type);
393  if (counter == counters_.jq_.end())
394  {
395  assert(false);
396  return;
397  }
398  {
399  std::lock_guard lock(counter->second.mutex);
400  ++counter->second.value.started;
401  counter->second.value.queuedDuration += dur;
402  }
404  if (instance >= 0 && instance < counters_.jobs_.size())
405  counters_.jobs_[instance] = {type, startTime};
406 }
407 
408 void
409 PerfLogImp::jobFinish(JobType const type, microseconds dur, int instance)
410 {
411  auto counter = counters_.jq_.find(type);
412  if (counter == counters_.jq_.end())
413  {
414  assert(false);
415  return;
416  }
417  {
418  std::lock_guard lock(counter->second.mutex);
419  ++counter->second.value.finished;
420  counter->second.value.runningDuration += dur;
421  }
423  if (instance >= 0 && instance < counters_.jobs_.size())
424  counters_.jobs_[instance] = {jtINVALID, steady_time_point()};
425 }
426 
427 void
428 PerfLogImp::resizeJobs(int const resize)
429 {
431  if (resize > counters_.jobs_.size())
433 }
434 
435 void
437 {
438  if (setup_.perfLog.empty())
439  return;
440 
441  std::lock_guard lock(mutex_);
442  rotate_ = true;
443  cond_.notify_one();
444 }
445 
446 void
448 {
449  if (setup_.perfLog.size())
451 }
452 
453 void
455 {
456  if (thread_.joinable())
457  {
458  {
459  std::lock_guard lock(mutex_);
460  stop_ = true;
461  cond_.notify_one();
462  }
463  thread_.join();
464  }
465 }
466 
467 //-----------------------------------------------------------------------------
468 
470 setup_PerfLog(Section const& section, boost::filesystem::path const& configDir)
471 {
472  PerfLog::Setup setup;
473  std::string perfLog;
474  set(perfLog, "perf_log", section);
475  if (perfLog.size())
476  {
477  setup.perfLog = boost::filesystem::path(perfLog);
478  if (setup.perfLog.is_relative())
479  {
480  setup.perfLog =
481  boost::filesystem::absolute(setup.perfLog, configDir);
482  }
483  }
484 
485  std::uint64_t logInterval;
486  if (get_if_exists(section, "log_interval", logInterval))
487  setup.logInterval = std::chrono::seconds(logInterval);
488  return setup;
489 }
490 
493  PerfLog::Setup const& setup,
494  beast::Journal journal,
495  std::function<void()>&& signalStop)
496 {
497  return std::make_unique<PerfLogImp>(setup, journal, std::move(signalStop));
498 }
499 
500 } // namespace perf
501 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
sstream
std::vector::resize
T resize(T... args)
ripple::perf::PerfLogImp::Counters::Jq::runningDuration
microseconds runningDuration
Definition: PerfLogImp.h:104
ripple::perf::PerfLogImp::Counters::Rpc::started
std::uint64_t started
Definition: PerfLogImp.h:85
std::string
STL class.
ripple::perf::PerfLogImp::Counters::Jq::finished
std::uint64_t finished
Definition: PerfLogImp.h:101
ripple::perf::PerfLogImp::lastLog_
system_time_point lastLog_
Definition: PerfLogImp.h:133
utility
ripple::JobTypes
Definition: JobTypes.h:32
ripple::perf::PerfLogImp::Counters::Jq::queued
std::uint64_t queued
Definition: PerfLogImp.h:99
ripple::perf::PerfLogImp::openLog
void openLog()
Definition: PerfLogImp.cpp:220
ripple::perf::PerfLog::Setup::perfLog
boost::filesystem::path perfLog
Definition: PerfLog.h:63
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::perf::PerfLogImp::Counters::jobsMutex_
std::mutex jobsMutex_
Definition: PerfLogImp.h:112
std::vector::reserve
T reserve(T... args)
Json::Compact
Decorator for streaming out compact json.
Definition: json_writer.h:316
ripple::perf::PerfLogImp::Counters::methodsMutex_
std::mutex methodsMutex_
Definition: PerfLogImp.h:114
std::vector
STL class.
std::vector::size
T size(T... args)
ripple::perf::PerfLogImp::jobFinish
void jobFinish(JobType const type, microseconds dur, int instance) override
Log job finishing.
Definition: PerfLogImp.cpp:409
ripple::perf::PerfLogImp::j_
const beast::Journal j_
Definition: PerfLogImp.h:126
ripple::perf::PerfLogImp::rotate
void rotate() override
Rotate perf log file.
Definition: PerfLogImp.cpp:436
std::chrono::microseconds
ripple::perf::PerfLogImp::cond_
std::condition_variable cond_
Definition: PerfLogImp.h:132
iterator
ripple::perf::PerfLogImp::Counters::Rpc::finished
std::uint64_t finished
Definition: PerfLogImp.h:86
std::lock_guard
STL class.
ripple::perf::PerfLogImp::Counters::Jq::started
std::uint64_t started
Definition: PerfLogImp.h:100
ripple::perf::PerfLogImp::Counters::methods_
std::unordered_map< std::uint64_t, MethodStart > methods_
Definition: PerfLogImp.h:113
std::function
ripple::perf::PerfLogImp::Counters::Rpc::errored
std::uint64_t errored
Definition: PerfLogImp.h:87
iostream
ripple::perf::PerfLogImp::hostname_
const std::string hostname_
Definition: PerfLogImp.h:134
ripple::perf::PerfLog::Setup
Configuration from [perf] section of rippled.cfg.
Definition: PerfLog.h:61
ripple::perf::make_PerfLog
std::unique_ptr< PerfLog > make_PerfLog(PerfLog::Setup const &setup, beast::Journal journal, std::function< void()> &&signalStop)
Definition: PerfLogImp.cpp:492
ripple::perf::PerfLogImp::jobStart
void jobStart(JobType const type, microseconds dur, steady_time_point startTime, int instance) override
Log job executing.
Definition: PerfLogImp.cpp:386
ripple::get_if_exists
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:384
std::vector::push_back
T push_back(T... args)
stdexcept
ripple::perf::PerfLogImp::rotate_
bool rotate_
Definition: PerfLogImp.h:136
std::thread::joinable
T joinable(T... args)
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
std::thread
STL class.
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::perf::PerfLogImp::mutex_
std::mutex mutex_
Definition: PerfLogImp.h:131
ripple::perf::PerfLogImp::Counters::jobs_
std::vector< std::pair< JobType, steady_time_point > > jobs_
Definition: PerfLogImp.h:111
ripple::perf::PerfLogImp::Counters::jq_
std::unordered_map< JobType, Locked< Jq > > jq_
Definition: PerfLogImp.h:110
std::unique_lock
STL class.
std::to_string
T to_string(T... args)
ripple::jtINVALID
@ jtINVALID
Definition: Job.h:36
ripple::set
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:313
ripple::JobTypes::name
static std::string const & name(JobType jt)
Definition: JobTypes.h:122
std::ofstream::close
T close(T... args)
std::chrono::time_point
cstdint
ripple::perf::PerfLogImp::thread_
std::thread thread_
Definition: PerfLogImp.h:130
ripple::ValStatus::current
@ current
This was a new validation and was added.
std::ofstream::open
T open(T... args)
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint64_t
atomic
ripple::perf::PerfLogImp::signalStop_
const std::function< void()> signalStop_
Definition: PerfLogImp.h:127
ripple::perf::PerfLogImp::PerfLogImp
PerfLogImp(Setup const &setup, beast::Journal journal, std::function< void()> &&signalStop)
Definition: PerfLogImp.cpp:304
ripple::perf::PerfLogImp::Counters::rpc_
std::unordered_map< std::string, Locked< Rpc > > rpc_
Definition: PerfLogImp.h:109
ripple::perf::PerfLogImp::Counters::Jq
Job Queue task performance counters.
Definition: PerfLogImp.h:95
ripple::perf::PerfLogImp::~PerfLogImp
~PerfLogImp() override
Definition: PerfLogImp.cpp:313
std::condition_variable::notify_one
T notify_one(T... args)
ripple::perf::setup_PerfLog
PerfLog::Setup setup_PerfLog(Section const &section, boost::filesystem::path const &configDir)
Definition: PerfLogImp.cpp:470
ripple::perf::PerfLogImp::Counters::Jq::queuedDuration
microseconds queuedDuration
Definition: PerfLogImp.h:103
beast::setCurrentThreadName
void setCurrentThreadName(std::string_view name)
Changes the name of the caller thread.
Definition: CurrentThreadName.cpp:119
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::perf::PerfLog::steady_time_point
std::chrono::time_point< steady_clock > steady_time_point
Definition: PerfLog.h:52
cstdlib
std::endl
T endl(T... args)
ripple::perf::PerfLogImp::stop_
bool stop_
Definition: PerfLogImp.h:135
ripple::perf::PerfLogImp::Counters::Rpc
RPC performance counters.
Definition: PerfLogImp.h:81
std::condition_variable::wait_until
T wait_until(T... args)
std
STL namespace.
ripple::perf::PerfLogImp::rpcEnd
void rpcEnd(std::string const &method, std::uint64_t const requestId, bool finish)
Definition: PerfLogImp.cpp:338
ripple::perf::PerfLogImp::rpcStart
void rpcStart(std::string const &method, std::uint64_t const requestId) override
Log start of RPC call.
Definition: PerfLogImp.cpp:319
ripple::perf::PerfLogImp::Counters::currentJson
Json::Value currentJson() const
Definition: PerfLogImp.cpp:171
std::chrono::microseconds::count
T count(T... args)
ripple::perf::PerfLogImp::run
void run()
Definition: PerfLogImp.cpp:254
optional
mutex
ripple::JobType
JobType
Definition: Job.h:34
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:38
ripple::perf::PerfLogImp::setup_
const Setup setup_
Definition: PerfLogImp.h:125
ripple::perf::PerfLogImp::stop
void stop() override
Definition: PerfLogImp.cpp:454
ripple::perf::PerfLogImp::Counters::Counters
Counters(std::vector< char const * > const &labels, JobTypes const &jobTypes)
Definition: PerfLogImp.cpp:43
ripple::perf::PerfLogImp::logFile_
std::ofstream logFile_
Definition: PerfLogImp.h:129
ripple::perf::PerfLogImp::counters_
Counters counters_
Definition: PerfLogImp.h:128
std::unique_ptr
STL class.
ripple::perf::PerfLogImp::Counters::Rpc::duration
microseconds duration
Definition: PerfLogImp.h:89
ripple::perf::PerfLogImp::jobQueue
void jobQueue(JobType const type) override
Log queued job.
Definition: PerfLogImp.cpp:373
unordered_map
ripple::perf::PerfLog::Setup::logInterval
milliseconds logInterval
Definition: PerfLog.h:65
ripple::perf::PerfLogImp::start
void start() override
Definition: PerfLogImp.cpp:447
ripple::JobTypes::size
Map::size_type size() const
Definition: JobTypes.h:146
std::ofstream::is_open
T is_open(T... args)
ripple::perf::PerfLogImp::resizeJobs
void resizeJobs(int const resize) override
Ensure enough room to store each currently executing job.
Definition: PerfLogImp.cpp:428
ripple::perf::PerfLogImp::Counters::countersJson
Json::Value countersJson() const
Definition: PerfLogImp.cpp:76
ripple::perf::PerfLogImp::report
void report()
Definition: PerfLogImp.cpp:279
std::thread::join
T join(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
string
std::chrono::steady_clock::now
T now(T... args)