rippled
Loading...
Searching...
No Matches
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 <xrpld/core/JobTypes.h>
21#include <xrpld/perflog/detail/PerfLogImp.h>
22#include <xrpl/basics/BasicConfig.h>
23#include <xrpl/beast/core/CurrentThreadName.h>
24#include <xrpl/beast/utility/Journal.h>
25#include <xrpl/json/json_writer.h>
26
27#include <atomic>
28#include <cstdint>
29#include <cstdlib>
30#include <iterator>
31#include <mutex>
32#include <optional>
33#include <sstream>
34#include <stdexcept>
35#include <string>
36#include <unordered_map>
37#include <utility>
38
39namespace ripple {
40namespace perf {
41
43 std::set<char const*> const& labels,
44 JobTypes const& jobTypes)
45{
46 {
47 // populateRpc
48 rpc_.reserve(labels.size());
49 for (std::string const label : labels)
50 {
51 auto const inserted = rpc_.emplace(label, Rpc()).second;
52 if (!inserted)
53 {
54 // Ensure that no other function populates this entry.
55 UNREACHABLE(
56 "ripple::perf::PerfLogImp::Counters::Counters : failed to "
57 "insert label");
58 }
59 }
60 }
61 {
62 // populateJq
63 jq_.reserve(jobTypes.size());
64 for (auto const& [jobType, _] : jobTypes)
65 {
66 auto const inserted = jq_.emplace(jobType, Jq()).second;
67 if (!inserted)
68 {
69 // Ensure that no other function populates this entry.
70 UNREACHABLE(
71 "ripple::perf::PerfLogImp::Counters::Counters : failed to "
72 "insert job type");
73 }
74 }
75 }
76}
77
80{
82 // totalRpc represents all rpc methods. All that started, finished, etc.
83 Rpc totalRpc;
84 for (auto const& proc : rpc_)
85 {
86 Rpc value;
87 {
88 std::lock_guard lock(proc.second.mutex);
89 if (!proc.second.value.started && !proc.second.value.finished &&
90 !proc.second.value.errored)
91 {
92 continue;
93 }
94 value = proc.second.value;
95 }
96
98 p[jss::started] = std::to_string(value.started);
99 totalRpc.started += value.started;
100 p[jss::finished] = std::to_string(value.finished);
101 totalRpc.finished += value.finished;
102 p[jss::errored] = std::to_string(value.errored);
103 totalRpc.errored += value.errored;
104 p[jss::duration_us] = std::to_string(value.duration.count());
105 totalRpc.duration += value.duration;
106 rpcobj[proc.first] = p;
107 }
108
109 if (totalRpc.started)
110 {
111 Json::Value totalRpcJson(Json::objectValue);
112 totalRpcJson[jss::started] = std::to_string(totalRpc.started);
113 totalRpcJson[jss::finished] = std::to_string(totalRpc.finished);
114 totalRpcJson[jss::errored] = std::to_string(totalRpc.errored);
115 totalRpcJson[jss::duration_us] =
116 std::to_string(totalRpc.duration.count());
117 rpcobj[jss::total] = totalRpcJson;
118 }
119
121 // totalJq represents all jobs. All enqueued, started, finished, etc.
122 Jq totalJq;
123 for (auto const& proc : jq_)
124 {
125 Jq value;
126 {
127 std::lock_guard lock(proc.second.mutex);
128 if (!proc.second.value.queued && !proc.second.value.started &&
129 !proc.second.value.finished)
130 {
131 continue;
132 }
133 value = proc.second.value;
134 }
135
137 j[jss::queued] = std::to_string(value.queued);
138 totalJq.queued += value.queued;
139 j[jss::started] = std::to_string(value.started);
140 totalJq.started += value.started;
141 j[jss::finished] = std::to_string(value.finished);
142 totalJq.finished += value.finished;
143 j[jss::queued_duration_us] =
145 totalJq.queuedDuration += value.queuedDuration;
146 j[jss::running_duration_us] =
148 totalJq.runningDuration += value.runningDuration;
149 jqobj[JobTypes::name(proc.first)] = j;
150 }
151
152 if (totalJq.queued)
153 {
154 Json::Value totalJqJson(Json::objectValue);
155 totalJqJson[jss::queued] = std::to_string(totalJq.queued);
156 totalJqJson[jss::started] = std::to_string(totalJq.started);
157 totalJqJson[jss::finished] = std::to_string(totalJq.finished);
158 totalJqJson[jss::queued_duration_us] =
160 totalJqJson[jss::running_duration_us] =
162 jqobj[jss::total] = totalJqJson;
163 }
164
166 // Be kind to reporting tools and let them expect rpc and jq objects
167 // even if empty.
168 counters[jss::rpc] = rpcobj;
169 counters[jss::job_queue] = jqobj;
170 return counters;
171}
172
175{
176 auto const present = steady_clock::now();
177
178 Json::Value jobsArray(Json::arrayValue);
179 auto const jobs = [this] {
180 std::lock_guard lock(jobsMutex_);
181 return jobs_;
182 }();
183
184 for (auto const& j : jobs)
185 {
186 if (j.first == jtINVALID)
187 continue;
189 jobj[jss::job] = JobTypes::name(j.first);
190 jobj[jss::duration_us] = std::to_string(
191 std::chrono::duration_cast<microseconds>(present - j.second)
192 .count());
193 jobsArray.append(jobj);
194 }
195
196 Json::Value methodsArray(Json::arrayValue);
198 {
199 std::lock_guard lock(methodsMutex_);
200 methods.reserve(methods_.size());
201 for (auto const& m : methods_)
202 methods.push_back(m.second);
203 }
204 for (auto m : methods)
205 {
207 methodobj[jss::method] = m.first;
208 methodobj[jss::duration_us] = std::to_string(
209 std::chrono::duration_cast<microseconds>(present - m.second)
210 .count());
211 methodsArray.append(methodobj);
212 }
213
215 current[jss::jobs] = jobsArray;
216 current[jss::methods] = methodsArray;
217 return current;
218}
219
220//-----------------------------------------------------------------------------
221
222void
224{
225 if (setup_.perfLog.empty())
226 return;
227
228 if (logFile_.is_open())
229 logFile_.close();
230
231 auto logDir = setup_.perfLog.parent_path();
232 if (!boost::filesystem::is_directory(logDir))
233 {
234 boost::system::error_code ec;
235 boost::filesystem::create_directories(logDir, ec);
236 if (ec)
237 {
238 JLOG(j_.fatal()) << "Unable to create performance log "
239 "directory "
240 << logDir << ": " << ec.message();
241 signalStop_();
242 return;
243 }
244 }
245
246 logFile_.open(setup_.perfLog.c_str(), std::ios::out | std::ios::app);
247
248 if (!logFile_)
249 {
250 JLOG(j_.fatal()) << "Unable to open performance log " << setup_.perfLog
251 << ".";
252 signalStop_();
253 }
254}
255
256void
258{
261
262 while (true)
263 {
264 {
266 if (cond_.wait_until(
267 lock, lastLog_ + setup_.logInterval, [&] { return stop_; }))
268 {
269 return;
270 }
271 if (rotate_)
272 {
273 openLog();
274 rotate_ = false;
275 }
276 }
277 report();
278 }
279}
280
281void
283{
284 if (!logFile_)
285 // If logFile_ is not writable do no further work.
286 return;
287
288 auto const present = system_clock::now();
289 if (present < lastLog_ + setup_.logInterval)
290 return;
291 lastLog_ = present;
292
294 report[jss::time] = to_string(std::chrono::floor<microseconds>(present));
295 {
297 report[jss::workers] =
298 static_cast<unsigned int>(counters_.jobs_.size());
299 }
300 report[jss::hostid] = hostname_;
301 report[jss::counters] = counters_.countersJson();
302 report[jss::nodestore] = Json::objectValue;
303 app_.getNodeStore().getCountsJson(report[jss::nodestore]);
304 report[jss::current_activities] = counters_.currentJson();
306
307 logFile_ << Json::Compact{std::move(report)} << std::endl;
308}
309
311 Setup const& setup,
312 Application& app,
313 beast::Journal journal,
314 std::function<void()>&& signalStop)
315 : setup_(setup), app_(app), j_(journal), signalStop_(std::move(signalStop))
316{
317 openLog();
318}
319
321{
322 stop();
323}
324
325void
326PerfLogImp::rpcStart(std::string const& method, std::uint64_t const requestId)
327{
328 auto counter = counters_.rpc_.find(method);
329 if (counter == counters_.rpc_.end())
330 {
331 UNREACHABLE("ripple::perf::PerfLogImp::rpcStart : valid method input");
332 return;
333 }
334
335 {
336 std::lock_guard lock(counter->second.mutex);
337 ++counter->second.value.started;
338 }
340 counters_.methods_[requestId] = {
341 counter->first.c_str(), steady_clock::now()};
342}
343
344void
346 std::string const& method,
347 std::uint64_t const requestId,
348 bool finish)
349{
350 auto counter = counters_.rpc_.find(method);
351 if (counter == counters_.rpc_.end())
352 {
353 UNREACHABLE("ripple::perf::PerfLogImp::rpcEnd : valid method input");
354 return;
355 }
356 steady_time_point startTime;
357 {
359 auto const e = counters_.methods_.find(requestId);
360 if (e != counters_.methods_.end())
361 {
362 startTime = e->second.second;
363 counters_.methods_.erase(e);
364 }
365 else
366 {
367 UNREACHABLE(
368 "ripple::perf::PerfLogImp::rpcEnd : valid requestId input");
369 }
370 }
371 std::lock_guard lock(counter->second.mutex);
372 if (finish)
373 ++counter->second.value.finished;
374 else
375 ++counter->second.value.errored;
376 counter->second.value.duration += std::chrono::duration_cast<microseconds>(
377 steady_clock::now() - startTime);
378}
379
380void
382{
383 auto counter = counters_.jq_.find(type);
384 if (counter == counters_.jq_.end())
385 {
386 UNREACHABLE(
387 "ripple::perf::PerfLogImp::jobQueue : valid job type input");
388 return;
389 }
390 std::lock_guard lock(counter->second.mutex);
391 ++counter->second.value.queued;
392}
393
394void
396 JobType const type,
397 microseconds dur,
398 steady_time_point startTime,
399 int instance)
400{
401 auto counter = counters_.jq_.find(type);
402 if (counter == counters_.jq_.end())
403 {
404 UNREACHABLE(
405 "ripple::perf::PerfLogImp::jobStart : valid job type input");
406 return;
407 }
408 {
409 std::lock_guard lock(counter->second.mutex);
410 ++counter->second.value.started;
411 counter->second.value.queuedDuration += dur;
412 }
414 if (instance >= 0 && instance < counters_.jobs_.size())
415 counters_.jobs_[instance] = {type, startTime};
416}
417
418void
419PerfLogImp::jobFinish(JobType const type, microseconds dur, int instance)
420{
421 auto counter = counters_.jq_.find(type);
422 if (counter == counters_.jq_.end())
423 {
424 UNREACHABLE(
425 "ripple::perf::PerfLogImp::jobFinish : valid job type input");
426 return;
427 }
428 {
429 std::lock_guard lock(counter->second.mutex);
430 ++counter->second.value.finished;
431 counter->second.value.runningDuration += dur;
432 }
434 if (instance >= 0 && instance < counters_.jobs_.size())
436}
437
438void
439PerfLogImp::resizeJobs(int const resize)
440{
442 if (resize > counters_.jobs_.size())
444}
445
446void
448{
449 if (setup_.perfLog.empty())
450 return;
451
453 rotate_ = true;
455}
456
457void
459{
460 if (setup_.perfLog.size())
462}
463
464void
466{
467 if (thread_.joinable())
468 {
469 {
471 stop_ = true;
473 }
474 thread_.join();
475 }
476}
477
478//-----------------------------------------------------------------------------
479
481setup_PerfLog(Section const& section, boost::filesystem::path const& configDir)
482{
483 PerfLog::Setup setup;
484 std::string perfLog;
485 set(perfLog, "perf_log", section);
486 if (perfLog.size())
487 {
488 setup.perfLog = boost::filesystem::path(perfLog);
489 if (setup.perfLog.is_relative())
490 {
491 setup.perfLog =
492 boost::filesystem::absolute(setup.perfLog, configDir);
493 }
494 }
495
496 std::uint64_t logInterval;
497 if (get_if_exists(section, "log_interval", logInterval))
498 setup.logInterval = std::chrono::seconds(logInterval);
499 return setup;
500}
501
504 PerfLog::Setup const& setup,
505 Application& app,
506 beast::Journal journal,
507 std::function<void()>&& signalStop)
508{
509 return std::make_unique<PerfLogImp>(
510 setup, app, journal, std::move(signalStop));
511}
512
513} // namespace perf
514} // namespace ripple
Decorator for streaming out compact json.
Definition: json_writer.h:318
Represents a JSON value.
Definition: json_value.h:148
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:897
A generic endpoint for log messages.
Definition: Journal.h:60
Stream fatal() const
Definition: Journal.h:352
virtual NodeStore::Database & getNodeStore()=0
virtual NetworkOPs & getOPs()=0
static std::string const & name(JobType jt)
Definition: JobTypes.h:135
Map::size_type size() const
Definition: JobTypes.h:159
virtual void stateAccounting(Json::Value &obj)=0
void getCountsJson(Json::Value &obj)
Definition: Database.cpp:267
Holds a collection of configuration values.
Definition: BasicConfig.h:45
std::string const hostname_
Definition: PerfLogImp.h:130
void rpcEnd(std::string const &method, std::uint64_t const requestId, bool finish)
Definition: PerfLogImp.cpp:345
void resizeJobs(int const resize) override
Ensure enough room to store each currently executing job.
Definition: PerfLogImp.cpp:439
beast::Journal const j_
Definition: PerfLogImp.h:122
std::function< void()> const signalStop_
Definition: PerfLogImp.h:123
void jobQueue(JobType const type) override
Log queued job.
Definition: PerfLogImp.cpp:381
void jobStart(JobType const type, microseconds dur, steady_time_point startTime, int instance) override
Log job executing.
Definition: PerfLogImp.cpp:395
void rpcStart(std::string const &method, std::uint64_t const requestId) override
Log start of RPC call.
Definition: PerfLogImp.cpp:326
void rotate() override
Rotate perf log file.
Definition: PerfLogImp.cpp:447
std::condition_variable cond_
Definition: PerfLogImp.h:128
void stop() override
Definition: PerfLogImp.cpp:465
void start() override
Definition: PerfLogImp.cpp:458
std::ofstream logFile_
Definition: PerfLogImp.h:125
PerfLogImp(Setup const &setup, Application &app, beast::Journal journal, std::function< void()> &&signalStop)
Definition: PerfLogImp.cpp:310
void jobFinish(JobType const type, microseconds dur, int instance) override
Log job finishing.
Definition: PerfLogImp.cpp:419
system_time_point lastLog_
Definition: PerfLogImp.h:129
std::chrono::time_point< steady_clock > steady_time_point
Definition: PerfLog.h:55
T close(T... args)
T endl(T... args)
T is_open(T... args)
T join(T... args)
T joinable(T... args)
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
PerfLog::Setup setup_PerfLog(Section const &section, boost::filesystem::path const &configDir)
Definition: PerfLogImp.cpp:481
std::unique_ptr< PerfLog > make_PerfLog(PerfLog::Setup const &setup, Application &app, beast::Journal journal, std::function< void()> &&signalStop)
Definition: PerfLogImp.cpp:503
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
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:315
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:386
@ current
This was a new validation and was added.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
JobType
Definition: Job.h:34
@ jtINVALID
Definition: Job.h:36
STL namespace.
T open(T... args)
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)
Job Queue task performance counters.
Definition: PerfLogImp.h:93
RPC performance counters.
Definition: PerfLogImp.h:79
std::unordered_map< std::string, Locked< Rpc > > rpc_
Definition: PerfLogImp.h:106
std::vector< std::pair< JobType, steady_time_point > > jobs_
Definition: PerfLogImp.h:108
std::unordered_map< std::uint64_t, MethodStart > methods_
Definition: PerfLogImp.h:110
Counters(std::set< char const * > const &labels, JobTypes const &jobTypes)
Definition: PerfLogImp.cpp:42
Json::Value countersJson() const
Definition: PerfLogImp.cpp:79
std::unordered_map< JobType, Locked< Jq > > jq_
Definition: PerfLogImp.h:107
Configuration from [perf] section of rippled.cfg.
Definition: PerfLog.h:65
boost::filesystem::path perfLog
Definition: PerfLog.h:66
milliseconds logInterval
Definition: PerfLog.h:68
T to_string(T... args)