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