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(date::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  Stoppable& parent,
307  beast::Journal journal,
308  std::function<void()>&& signalStop)
309  : Stoppable("PerfLogImp", parent)
310  , setup_(setup)
311  , j_(journal)
312  , signalStop_(std::move(signalStop))
313 {
314  openLog();
315 }
316 
318 {
319  onStop();
320 }
321 
322 void
323 PerfLogImp::rpcStart(std::string const& method, std::uint64_t const requestId)
324 {
325  auto counter = counters_.rpc_.find(method);
326  if (counter == counters_.rpc_.end())
327  {
328  assert(false);
329  return;
330  }
331 
332  {
333  std::lock_guard lock(counter->second.mutex);
334  ++counter->second.value.started;
335  }
337  counters_.methods_[requestId] = {
338  counter->first.c_str(), steady_clock::now()};
339 }
340 
341 void
343  std::string const& method,
344  std::uint64_t const requestId,
345  bool finish)
346 {
347  auto counter = counters_.rpc_.find(method);
348  if (counter == counters_.rpc_.end())
349  {
350  assert(false);
351  return;
352  }
353  steady_time_point startTime;
354  {
356  auto const e = counters_.methods_.find(requestId);
357  if (e != counters_.methods_.end())
358  {
359  startTime = e->second.second;
360  counters_.methods_.erase(e);
361  }
362  else
363  {
364  assert(false);
365  }
366  }
367  std::lock_guard lock(counter->second.mutex);
368  if (finish)
369  ++counter->second.value.finished;
370  else
371  ++counter->second.value.errored;
372  counter->second.value.duration += std::chrono::duration_cast<microseconds>(
373  steady_clock::now() - startTime);
374 }
375 
376 void
378 {
379  auto counter = counters_.jq_.find(type);
380  if (counter == counters_.jq_.end())
381  {
382  assert(false);
383  return;
384  }
385  std::lock_guard lock(counter->second.mutex);
386  ++counter->second.value.queued;
387 }
388 
389 void
391  JobType const type,
392  microseconds dur,
393  steady_time_point startTime,
394  int instance)
395 {
396  auto counter = counters_.jq_.find(type);
397  if (counter == counters_.jq_.end())
398  {
399  assert(false);
400  return;
401  }
402  {
403  std::lock_guard lock(counter->second.mutex);
404  ++counter->second.value.started;
405  counter->second.value.queuedDuration += dur;
406  }
408  if (instance >= 0 && instance < counters_.jobs_.size())
409  counters_.jobs_[instance] = {type, startTime};
410 }
411 
412 void
413 PerfLogImp::jobFinish(JobType const type, microseconds dur, int instance)
414 {
415  auto counter = counters_.jq_.find(type);
416  if (counter == counters_.jq_.end())
417  {
418  assert(false);
419  return;
420  }
421  {
422  std::lock_guard lock(counter->second.mutex);
423  ++counter->second.value.finished;
424  counter->second.value.runningDuration += dur;
425  }
427  if (instance >= 0 && instance < counters_.jobs_.size())
428  counters_.jobs_[instance] = {jtINVALID, steady_time_point()};
429 }
430 
431 void
432 PerfLogImp::resizeJobs(int const resize)
433 {
435  if (resize > counters_.jobs_.size())
437 }
438 
439 void
441 {
442  if (setup_.perfLog.empty())
443  return;
444 
445  std::lock_guard lock(mutex_);
446  rotate_ = true;
447  cond_.notify_one();
448 }
449 
450 void
452 {
453  if (setup_.perfLog.size())
455 }
456 
457 void
459 {
460  if (thread_.joinable())
461  {
462  {
463  std::lock_guard lock(mutex_);
464  stop_ = true;
465  cond_.notify_one();
466  }
467  thread_.join();
468  }
469 }
470 
471 void
473 {
474  stopped();
475 }
476 
477 //-----------------------------------------------------------------------------
478 
480 setup_PerfLog(Section const& section, boost::filesystem::path const& configDir)
481 {
482  PerfLog::Setup setup;
483  std::string perfLog;
484  set(perfLog, "perf_log", section);
485  if (perfLog.size())
486  {
487  setup.perfLog = boost::filesystem::path(perfLog);
488  if (setup.perfLog.is_relative())
489  {
490  setup.perfLog =
491  boost::filesystem::absolute(setup.perfLog, configDir);
492  }
493  }
494 
495  std::uint64_t logInterval;
496  if (get_if_exists(section, "log_interval", logInterval))
497  setup.logInterval = std::chrono::seconds(logInterval);
498  return setup;
499 }
500 
503  PerfLog::Setup const& setup,
504  Stoppable& parent,
505  beast::Journal journal,
506  std::function<void()>&& signalStop)
507 {
508  return std::make_unique<PerfLogImp>(
509  setup, parent, journal, std::move(signalStop));
510 }
511 
512 } // namespace perf
513 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:43
sstream
std::vector::resize
T resize(T... args)
ripple::perf::PerfLogImp::Counters::Jq::runningDuration
microseconds runningDuration
Definition: PerfLogImp.h:105
ripple::perf::PerfLogImp::Counters::Rpc::started
std::uint64_t started
Definition: PerfLogImp.h:86
std::string
STL class.
ripple::perf::PerfLogImp::Counters::Jq::finished
std::uint64_t finished
Definition: PerfLogImp.h:102
ripple::perf::PerfLogImp::lastLog_
system_time_point lastLog_
Definition: PerfLogImp.h:134
utility
ripple::Stoppable::stopped
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
Definition: Stoppable.cpp:72
ripple::JobTypes
Definition: JobTypes.h:32
ripple::perf::PerfLogImp::Counters::Jq::queued
std::uint64_t queued
Definition: PerfLogImp.h:100
ripple::perf::PerfLogImp::openLog
void openLog()
Definition: PerfLogImp.cpp:220
ripple::perf::PerfLog::Setup::perfLog
boost::filesystem::path perfLog
Definition: PerfLog.h:62
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::perf::PerfLogImp::Counters::jobsMutex_
std::mutex jobsMutex_
Definition: PerfLogImp.h:113
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:115
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:413
ripple::perf::PerfLogImp::j_
const beast::Journal j_
Definition: PerfLogImp.h:127
ripple::perf::PerfLogImp::rotate
void rotate() override
Rotate perf log file.
Definition: PerfLogImp.cpp:440
std::chrono::microseconds
ripple::perf::PerfLogImp::cond_
std::condition_variable cond_
Definition: PerfLogImp.h:133
iterator
ripple::perf::PerfLogImp::PerfLogImp
PerfLogImp(Setup const &setup, Stoppable &parent, beast::Journal journal, std::function< void()> &&signalStop)
Definition: PerfLogImp.cpp:304
ripple::perf::PerfLogImp::Counters::Rpc::finished
std::uint64_t finished
Definition: PerfLogImp.h:87
std::lock_guard
STL class.
ripple::perf::PerfLogImp::Counters::Jq::started
std::uint64_t started
Definition: PerfLogImp.h:101
ripple::perf::PerfLogImp::Counters::methods_
std::unordered_map< std::uint64_t, MethodStart > methods_
Definition: PerfLogImp.h:114
std::function
ripple::perf::make_PerfLog
std::unique_ptr< PerfLog > make_PerfLog(PerfLog::Setup const &setup, Stoppable &parent, beast::Journal journal, std::function< void()> &&signalStop)
Definition: PerfLogImp.cpp:502
ripple::perf::PerfLogImp::Counters::Rpc::errored
std::uint64_t errored
Definition: PerfLogImp.h:88
iostream
ripple::perf::PerfLogImp::hostname_
const std::string hostname_
Definition: PerfLogImp.h:135
ripple::perf::PerfLog::Setup
Configuration from [perf] section of rippled.cfg.
Definition: PerfLog.h:60
ripple::perf::PerfLogImp::jobStart
void jobStart(JobType const type, microseconds dur, steady_time_point startTime, int instance) override
Log job executing.
Definition: PerfLogImp.cpp:390
ripple::get_if_exists
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:347
std::vector::push_back
T push_back(T... args)
stdexcept
ripple::perf::PerfLogImp::rotate_
bool rotate_
Definition: PerfLogImp.h:137
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
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:201
std::thread
STL class.
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::perf::PerfLogImp::onStop
void onStop() override
Override called when the stop notification is issued.
Definition: PerfLogImp.cpp:458
ripple::perf::PerfLogImp::mutex_
std::mutex mutex_
Definition: PerfLogImp.h:132
ripple::perf::PerfLogImp::Counters::jobs_
std::vector< std::pair< JobType, steady_time_point > > jobs_
Definition: PerfLogImp.h:112
ripple::perf::PerfLogImp::Counters::jq_
std::unordered_map< JobType, Locked< Jq > > jq_
Definition: PerfLogImp.h:111
std::unique_lock
STL class.
std::to_string
T to_string(T... args)
ripple::jtINVALID
@ jtINVALID
Definition: Job.h:35
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:276
ripple::JobTypes::name
static std::string const & name(JobType jt)
Definition: JobTypes.h:110
std::ofstream::close
T close(T... args)
std::chrono::time_point
cstdint
ripple::perf::PerfLogImp::thread_
std::thread thread_
Definition: PerfLogImp.h:131
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:128
ripple::perf::PerfLogImp::Counters::rpc_
std::unordered_map< std::string, Locked< Rpc > > rpc_
Definition: PerfLogImp.h:110
ripple::perf::PerfLogImp::Counters::Jq
Job Queue task performance counters.
Definition: PerfLogImp.h:96
ripple::perf::PerfLogImp::~PerfLogImp
~PerfLogImp() override
Definition: PerfLogImp.cpp:317
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:480
ripple::perf::PerfLogImp::Counters::Jq::queuedDuration
microseconds queuedDuration
Definition: PerfLogImp.h:104
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:51
cstdlib
std::endl
T endl(T... args)
ripple::perf::PerfLogImp::stop_
bool stop_
Definition: PerfLogImp.h:136
ripple::perf::PerfLogImp::Counters::Rpc
RPC performance counters.
Definition: PerfLogImp.h:82
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:342
ripple::perf::PerfLogImp::rpcStart
void rpcStart(std::string const &method, std::uint64_t const requestId) override
Log start of RPC call.
Definition: PerfLogImp.cpp:323
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:33
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:126
ripple::perf::PerfLogImp::onChildrenStopped
void onChildrenStopped() override
Override called when all children have stopped.
Definition: PerfLogImp.cpp:472
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:130
ripple::perf::PerfLogImp::counters_
Counters counters_
Definition: PerfLogImp.h:129
std::unique_ptr
STL class.
ripple::perf::PerfLogImp::Counters::Rpc::duration
microseconds duration
Definition: PerfLogImp.h:90
ripple::perf::PerfLogImp::jobQueue
void jobQueue(JobType const type) override
Log queued job.
Definition: PerfLogImp.cpp:377
unordered_map
ripple::perf::PerfLog::Setup::logInterval
milliseconds logInterval
Definition: PerfLog.h:64
ripple::JobTypes::size
Map::size_type size() const
Definition: JobTypes.h:134
std::ofstream::is_open
T is_open(T... args)
ripple::perf::PerfLogImp::onStart
void onStart() override
Override called during start.
Definition: PerfLogImp.cpp:451
ripple::perf::PerfLogImp::resizeJobs
void resizeJobs(int const resize) override
Ensure enough room to store each currently executing job.
Definition: PerfLogImp.cpp:432
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)