1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "dbus/dbus_statistics.h"
10 #include "base/logging.h"
11 #include "base/notreached.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/time/time.h"
22 std::string interface;
26 bool operator<(const StatKey& lhs, const StatKey& rhs) {
27 return std::tie(lhs.service, lhs.interface, lhs.method) <
28 std::tie(rhs.service, rhs.interface, rhs.method);
32 int sent_method_calls = 0;
33 int received_signals = 0;
34 int sent_blocking_method_calls = 0;
37 using StatMap = std::map<StatKey, StatValue>;
39 //------------------------------------------------------------------------------
42 // Simple class for gathering DBus usage statistics.
43 class DBusStatistics {
46 : start_time_(base::Time::Now()),
47 origin_thread_id_(base::PlatformThread::CurrentId()) {
50 DBusStatistics(const DBusStatistics&) = delete;
51 DBusStatistics& operator=(const DBusStatistics&) = delete;
54 DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
57 // Enum to specify which field in Stat to increment in AddStat.
59 TYPE_SENT_METHOD_CALLS,
60 TYPE_RECEIVED_SIGNALS,
61 TYPE_SENT_BLOCKING_METHOD_CALLS
64 // Add a call to |method| for |interface|. See also MethodCall in message.h.
65 void AddStat(const std::string& service,
66 const std::string& interface,
67 const std::string& method,
69 if (base::PlatformThread::CurrentId() != origin_thread_id_) {
70 DVLOG(1) << "Ignoring DBusStatistics::AddStat call from thread: "
71 << base::PlatformThread::CurrentId();
74 StatValue* stat = GetStats(service, interface, method, true);
76 if (type == TYPE_SENT_METHOD_CALLS)
77 ++stat->sent_method_calls;
78 else if (type == TYPE_RECEIVED_SIGNALS)
79 ++stat->received_signals;
80 else if (type == TYPE_SENT_BLOCKING_METHOD_CALLS)
81 ++stat->sent_blocking_method_calls;
86 // Look up the Stat entry in |stats_|. If |add_stat| is true, add a new entry
87 // if one does not already exist.
88 StatValue* GetStats(const std::string& service,
89 const std::string& interface,
90 const std::string& method,
92 DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
94 StatKey key = {service, interface, method};
95 auto it = stats_.find(key);
96 if (it != stats_.end())
102 return &(stats_[key]);
105 StatMap& stats() { return stats_; }
106 base::Time start_time() { return start_time_; }
110 base::Time start_time_;
111 base::PlatformThreadId origin_thread_id_;
114 DBusStatistics* g_dbus_statistics = nullptr;
118 //------------------------------------------------------------------------------
120 namespace statistics {
123 if (g_dbus_statistics)
124 delete g_dbus_statistics; // reset statistics
125 g_dbus_statistics = new DBusStatistics();
129 delete g_dbus_statistics;
130 g_dbus_statistics = nullptr;
133 void AddSentMethodCall(const std::string& service,
134 const std::string& interface,
135 const std::string& method) {
136 if (!g_dbus_statistics)
138 g_dbus_statistics->AddStat(
139 service, interface, method, DBusStatistics::TYPE_SENT_METHOD_CALLS);
142 void AddReceivedSignal(const std::string& service,
143 const std::string& interface,
144 const std::string& method) {
145 if (!g_dbus_statistics)
147 g_dbus_statistics->AddStat(
148 service, interface, method, DBusStatistics::TYPE_RECEIVED_SIGNALS);
151 void AddBlockingSentMethodCall(const std::string& service,
152 const std::string& interface,
153 const std::string& method) {
154 if (!g_dbus_statistics)
156 g_dbus_statistics->AddStat(
157 service, interface, method,
158 DBusStatistics::TYPE_SENT_BLOCKING_METHOD_CALLS);
161 // NOTE: If the output format is changed, be certain to change the test
162 // expectations as well.
163 std::string GetAsString(ShowInString show, FormatString format) {
164 if (!g_dbus_statistics)
165 return "DBusStatistics not initialized.";
167 const StatMap& stats = g_dbus_statistics->stats();
169 return "No DBus calls.";
171 base::TimeDelta dtime = base::Time::Now() - g_dbus_statistics->start_time();
172 int dminutes = dtime.InMinutes();
173 dminutes = std::max(dminutes, 1);
176 int sent = 0, received = 0, sent_blocking = 0;
177 // Stats are stored in order by service, then interface, then method.
178 for (auto iter = stats.begin(); iter != stats.end();) {
179 auto cur_iter = iter;
180 auto next_iter = ++iter;
181 const StatKey& stat_key = cur_iter->first;
182 const StatValue& stat = cur_iter->second;
183 sent += stat.sent_method_calls;
184 received += stat.received_signals;
185 sent_blocking += stat.sent_blocking_method_calls;
186 // If this is not the last stat, and if the next stat matches the current
188 if (next_iter != stats.end() &&
189 next_iter->first.service == stat_key.service &&
190 (show < SHOW_INTERFACE ||
191 next_iter->first.interface == stat_key.interface) &&
192 (show < SHOW_METHOD || next_iter->first.method == stat_key.method))
195 if (!sent && !received && !sent_blocking)
196 continue; // No stats collected for this line, skip it and continue.
198 // Add a line to the result and clear the counts.
200 if (show == SHOW_SERVICE) {
201 line += stat_key.service;
203 // The interface usually includes the service so don't show both.
204 line += stat_key.interface;
205 if (show >= SHOW_METHOD)
206 line += "." + stat_key.method;
208 line += base::StringPrintf(":");
210 line += base::StringPrintf(" Sent (BLOCKING):");
211 if (format == FORMAT_TOTALS)
212 line += base::StringPrintf(" %d", sent_blocking);
213 else if (format == FORMAT_PER_MINUTE)
214 line += base::StringPrintf(" %d/min", sent_blocking / dminutes);
215 else if (format == FORMAT_ALL)
216 line += base::StringPrintf(" %d (%d/min)",
217 sent_blocking, sent_blocking / dminutes);
220 line += base::StringPrintf(" Sent:");
221 if (format == FORMAT_TOTALS)
222 line += base::StringPrintf(" %d", sent);
223 else if (format == FORMAT_PER_MINUTE)
224 line += base::StringPrintf(" %d/min", sent / dminutes);
225 else if (format == FORMAT_ALL)
226 line += base::StringPrintf(" %d (%d/min)", sent, sent / dminutes);
229 line += base::StringPrintf(" Received:");
230 if (format == FORMAT_TOTALS)
231 line += base::StringPrintf(" %d", received);
232 else if (format == FORMAT_PER_MINUTE)
233 line += base::StringPrintf(" %d/min", received / dminutes);
234 else if (format == FORMAT_ALL)
235 line += base::StringPrintf(
236 " %d (%d/min)", received, received / dminutes);
238 result += line + "\n";
248 bool GetCalls(const std::string& service,
249 const std::string& interface,
250 const std::string& method,
254 if (!g_dbus_statistics)
257 g_dbus_statistics->GetStats(service, interface, method, false);
260 *sent = stat->sent_method_calls;
261 *received = stat->received_signals;
262 *blocking = stat->sent_blocking_method_calls;
266 } // namespace testing
268 } // namespace statistics