1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/time.h"
20 // Used to store dbus statistics sorted alphabetically by service, interface,
21 // then method (using std::string <).
23 Stat(const std::string
& service
,
24 const std::string
& interface
,
25 const std::string
& method
)
31 sent_blocking_method_calls(0) {
34 std::string interface
;
36 int sent_method_calls
;
38 int sent_blocking_method_calls
;
40 bool Compare(const Stat
& other
) const {
41 if (service
!= other
.service
)
42 return service
< other
.service
;
43 if (interface
!= other
.interface
)
44 return interface
< other
.interface
;
45 return method
< other
.method
;
49 bool operator()(Stat
* lhs
, Stat
* rhs
) const {
51 return lhs
->Compare(*rhs
);
56 typedef std::set
<Stat
*, Stat::PtrCompare
> StatSet
;
58 //------------------------------------------------------------------------------
61 // Simple class for gathering DBus usage statistics.
62 class DBusStatistics
{
65 : start_time_(base::Time::Now()),
66 origin_thread_id_(base::PlatformThread::CurrentId()) {
70 DCHECK_EQ(origin_thread_id_
, base::PlatformThread::CurrentId());
71 STLDeleteContainerPointers(stats_
.begin(), stats_
.end());
74 // Enum to specify which field in Stat to increment in AddStat
76 TYPE_SENT_METHOD_CALLS
,
77 TYPE_RECEIVED_SIGNALS
,
78 TYPE_SENT_BLOCKING_METHOD_CALLS
81 // Add a call to |method| for |interface|. See also MethodCall in message.h.
82 void AddStat(const std::string
& service
,
83 const std::string
& interface
,
84 const std::string
& method
,
86 if (base::PlatformThread::CurrentId() != origin_thread_id_
) {
87 DLOG(WARNING
) << "Ignoring DBusStatistics::AddStat call from thread: "
88 << base::PlatformThread::CurrentId();
91 Stat
* stat
= GetStat(service
, interface
, method
, true);
93 if (type
== TYPE_SENT_METHOD_CALLS
)
94 ++stat
->sent_method_calls
;
95 else if (type
== TYPE_RECEIVED_SIGNALS
)
96 ++stat
->received_signals
;
97 else if (type
== TYPE_SENT_BLOCKING_METHOD_CALLS
)
98 ++stat
->sent_blocking_method_calls
;
103 // Look up the Stat entry in |stats_|. If |add_stat| is true, add a new entry
104 // if one does not already exist.
105 Stat
* GetStat(const std::string
& service
,
106 const std::string
& interface
,
107 const std::string
& method
,
109 DCHECK_EQ(origin_thread_id_
, base::PlatformThread::CurrentId());
110 scoped_ptr
<Stat
> stat(new Stat(service
, interface
, method
));
111 StatSet::iterator found
= stats_
.find(stat
.get());
112 if (found
!= stats_
.end())
116 found
= stats_
.insert(stat
.release()).first
;
120 StatSet
& stats() { return stats_
; }
121 base::Time
start_time() { return start_time_
; }
125 base::Time start_time_
;
126 base::PlatformThreadId origin_thread_id_
;
128 DISALLOW_COPY_AND_ASSIGN(DBusStatistics
);
131 DBusStatistics
* g_dbus_statistics
= NULL
;
135 //------------------------------------------------------------------------------
137 namespace statistics
{
140 if (g_dbus_statistics
)
141 delete g_dbus_statistics
; // reset statistics
142 g_dbus_statistics
= new DBusStatistics();
146 delete g_dbus_statistics
;
147 g_dbus_statistics
= NULL
;
150 void AddSentMethodCall(const std::string
& service
,
151 const std::string
& interface
,
152 const std::string
& method
) {
153 if (!g_dbus_statistics
)
155 g_dbus_statistics
->AddStat(
156 service
, interface
, method
, DBusStatistics::TYPE_SENT_METHOD_CALLS
);
159 void AddReceivedSignal(const std::string
& service
,
160 const std::string
& interface
,
161 const std::string
& method
) {
162 if (!g_dbus_statistics
)
164 g_dbus_statistics
->AddStat(
165 service
, interface
, method
, DBusStatistics::TYPE_RECEIVED_SIGNALS
);
168 void AddBlockingSentMethodCall(const std::string
& service
,
169 const std::string
& interface
,
170 const std::string
& method
) {
171 if (!g_dbus_statistics
)
173 g_dbus_statistics
->AddStat(
174 service
, interface
, method
,
175 DBusStatistics::TYPE_SENT_BLOCKING_METHOD_CALLS
);
178 // NOTE: If the output format is changed, be certain to change the test
179 // expectations as well.
180 std::string
GetAsString(ShowInString show
, FormatString format
) {
181 if (!g_dbus_statistics
)
182 return "DBusStatistics not initialized.";
184 const StatSet
& stats
= g_dbus_statistics
->stats();
186 return "No DBus calls.";
188 base::TimeDelta dtime
= base::Time::Now() - g_dbus_statistics
->start_time();
189 int dminutes
= dtime
.InMinutes();
190 dminutes
= std::max(dminutes
, 1);
193 int sent
= 0, received
= 0, sent_blocking
= 0;
194 // Stats are stored in order by service, then interface, then method.
195 for (StatSet::const_iterator iter
= stats
.begin(); iter
!= stats
.end(); ) {
196 StatSet::const_iterator cur_iter
= iter
;
197 StatSet::const_iterator next_iter
= ++iter
;
198 const Stat
* stat
= *cur_iter
;
199 sent
+= stat
->sent_method_calls
;
200 received
+= stat
->received_signals
;
201 sent_blocking
+= stat
->sent_blocking_method_calls
;
202 // If this is not the last stat, and if the next stat matches the current
204 if (next_iter
!= stats
.end() &&
205 (*next_iter
)->service
== stat
->service
&&
206 (show
< SHOW_INTERFACE
|| (*next_iter
)->interface
== stat
->interface
) &&
207 (show
< SHOW_METHOD
|| (*next_iter
)->method
== stat
->method
))
210 if (!sent
&& !received
&& !sent_blocking
)
211 continue; // No stats collected for this line, skip it and continue.
213 // Add a line to the result and clear the counts.
215 if (show
== SHOW_SERVICE
) {
216 line
+= stat
->service
;
218 // The interface usually includes the service so don't show both.
219 line
+= stat
->interface
;
220 if (show
>= SHOW_METHOD
)
221 line
+= "." + stat
->method
;
223 line
+= base::StringPrintf(":");
225 line
+= base::StringPrintf(" Sent (BLOCKING):");
226 if (format
== FORMAT_TOTALS
)
227 line
+= base::StringPrintf(" %d", sent_blocking
);
228 else if (format
== FORMAT_PER_MINUTE
)
229 line
+= base::StringPrintf(" %d/min", sent_blocking
/ dminutes
);
230 else if (format
== FORMAT_ALL
)
231 line
+= base::StringPrintf(" %d (%d/min)",
232 sent_blocking
, sent_blocking
/ dminutes
);
235 line
+= base::StringPrintf(" Sent:");
236 if (format
== FORMAT_TOTALS
)
237 line
+= base::StringPrintf(" %d", sent
);
238 else if (format
== FORMAT_PER_MINUTE
)
239 line
+= base::StringPrintf(" %d/min", sent
/ dminutes
);
240 else if (format
== FORMAT_ALL
)
241 line
+= base::StringPrintf(" %d (%d/min)", sent
, sent
/ dminutes
);
244 line
+= base::StringPrintf(" Received:");
245 if (format
== FORMAT_TOTALS
)
246 line
+= base::StringPrintf(" %d", received
);
247 else if (format
== FORMAT_PER_MINUTE
)
248 line
+= base::StringPrintf(" %d/min", received
/ dminutes
);
249 else if (format
== FORMAT_ALL
)
250 line
+= base::StringPrintf(
251 " %d (%d/min)", received
, received
/ dminutes
);
253 result
+= line
+ "\n";
263 bool GetCalls(const std::string
& service
,
264 const std::string
& interface
,
265 const std::string
& method
,
269 if (!g_dbus_statistics
)
271 Stat
* stat
= g_dbus_statistics
->GetStat(service
, interface
, method
, false);
274 *sent
= stat
->sent_method_calls
;
275 *received
= stat
->received_signals
;
276 *blocking
= stat
->sent_blocking_method_calls
;
280 } // namespace testing
282 } // namespace statistics