• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "dbus/dbus_statistics.h"
6 
7 #include <map>
8 #include <tuple>
9 
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/time/time.h"
15 
16 namespace dbus {
17 
18 namespace {
19 
20 struct StatKey {
21   std::string service;
22   std::string interface;
23   std::string method;
24 };
25 
operator <(const StatKey & lhs,const StatKey & rhs)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);
29 }
30 
31 struct StatValue {
32   int sent_method_calls = 0;
33   int received_signals = 0;
34   int sent_blocking_method_calls = 0;
35 };
36 
37 using StatMap = std::map<StatKey, StatValue>;
38 
39 //------------------------------------------------------------------------------
40 // DBusStatistics
41 
42 // Simple class for gathering DBus usage statistics.
43 class DBusStatistics {
44  public:
DBusStatistics()45   DBusStatistics()
46       : start_time_(base::Time::Now()),
47         origin_thread_id_(base::PlatformThread::CurrentId()) {
48   }
49 
~DBusStatistics()50   ~DBusStatistics() {
51     DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
52   }
53 
54   // Enum to specify which field in Stat to increment in AddStat.
55   enum StatType {
56     TYPE_SENT_METHOD_CALLS,
57     TYPE_RECEIVED_SIGNALS,
58     TYPE_SENT_BLOCKING_METHOD_CALLS
59   };
60 
61   // Add a call to |method| for |interface|. See also MethodCall in message.h.
AddStat(const std::string & service,const std::string & interface,const std::string & method,StatType type)62   void AddStat(const std::string& service,
63                const std::string& interface,
64                const std::string& method,
65                StatType type) {
66     if (base::PlatformThread::CurrentId() != origin_thread_id_) {
67       DVLOG(1) << "Ignoring DBusStatistics::AddStat call from thread: "
68                << base::PlatformThread::CurrentId();
69       return;
70     }
71     StatValue* stat = GetStats(service, interface, method, true);
72     DCHECK(stat);
73     if (type == TYPE_SENT_METHOD_CALLS)
74       ++stat->sent_method_calls;
75     else if (type == TYPE_RECEIVED_SIGNALS)
76       ++stat->received_signals;
77     else if (type == TYPE_SENT_BLOCKING_METHOD_CALLS)
78       ++stat->sent_blocking_method_calls;
79     else
80       NOTREACHED();
81   }
82 
83   // Look up the Stat entry in |stats_|. If |add_stat| is true, add a new entry
84   // if one does not already exist.
GetStats(const std::string & service,const std::string & interface,const std::string & method,bool add_stat)85   StatValue* GetStats(const std::string& service,
86                       const std::string& interface,
87                       const std::string& method,
88                       bool add_stat) {
89     DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
90 
91     StatKey key = {service, interface, method};
92     auto it = stats_.find(key);
93     if (it != stats_.end())
94       return &(it->second);
95 
96     if (!add_stat)
97       return nullptr;
98 
99     return &(stats_[key]);
100   }
101 
stats()102   StatMap& stats() { return stats_; }
start_time()103   base::Time start_time() { return start_time_; }
104 
105  private:
106   StatMap stats_;
107   base::Time start_time_;
108   base::PlatformThreadId origin_thread_id_;
109 
110   DISALLOW_COPY_AND_ASSIGN(DBusStatistics);
111 };
112 
113 DBusStatistics* g_dbus_statistics = nullptr;
114 
115 }  // namespace
116 
117 //------------------------------------------------------------------------------
118 
119 namespace statistics {
120 
Initialize()121 void Initialize() {
122   if (g_dbus_statistics)
123     delete g_dbus_statistics;  // reset statistics
124   g_dbus_statistics = new DBusStatistics();
125 }
126 
Shutdown()127 void Shutdown() {
128   delete g_dbus_statistics;
129   g_dbus_statistics = nullptr;
130 }
131 
AddSentMethodCall(const std::string & service,const std::string & interface,const std::string & method)132 void AddSentMethodCall(const std::string& service,
133                        const std::string& interface,
134                        const std::string& method) {
135   if (!g_dbus_statistics)
136     return;
137   g_dbus_statistics->AddStat(
138       service, interface, method, DBusStatistics::TYPE_SENT_METHOD_CALLS);
139 }
140 
AddReceivedSignal(const std::string & service,const std::string & interface,const std::string & method)141 void AddReceivedSignal(const std::string& service,
142                        const std::string& interface,
143                        const std::string& method) {
144   if (!g_dbus_statistics)
145     return;
146   g_dbus_statistics->AddStat(
147       service, interface, method, DBusStatistics::TYPE_RECEIVED_SIGNALS);
148 }
149 
AddBlockingSentMethodCall(const std::string & service,const std::string & interface,const std::string & method)150 void AddBlockingSentMethodCall(const std::string& service,
151                                const std::string& interface,
152                                const std::string& method) {
153   if (!g_dbus_statistics)
154     return;
155   g_dbus_statistics->AddStat(
156       service, interface, method,
157       DBusStatistics::TYPE_SENT_BLOCKING_METHOD_CALLS);
158 }
159 
160 // NOTE: If the output format is changed, be certain to change the test
161 // expectations as well.
GetAsString(ShowInString show,FormatString format)162 std::string GetAsString(ShowInString show, FormatString format) {
163   if (!g_dbus_statistics)
164     return "DBusStatistics not initialized.";
165 
166   const StatMap& stats = g_dbus_statistics->stats();
167   if (stats.empty())
168     return "No DBus calls.";
169 
170   base::TimeDelta dtime = base::Time::Now() - g_dbus_statistics->start_time();
171   int dminutes = dtime.InMinutes();
172   dminutes = std::max(dminutes, 1);
173 
174   std::string result;
175   int sent = 0, received = 0, sent_blocking = 0;
176   // Stats are stored in order by service, then interface, then method.
177   for (auto iter = stats.begin(); iter != stats.end();) {
178     auto cur_iter = iter;
179     auto next_iter = ++iter;
180     const StatKey& stat_key = cur_iter->first;
181     const StatValue& stat = cur_iter->second;
182     sent += stat.sent_method_calls;
183     received += stat.received_signals;
184     sent_blocking += stat.sent_blocking_method_calls;
185     // If this is not the last stat, and if the next stat matches the current
186     // stat, continue.
187     if (next_iter != stats.end() &&
188         next_iter->first.service == stat_key.service &&
189         (show < SHOW_INTERFACE ||
190          next_iter->first.interface == stat_key.interface) &&
191         (show < SHOW_METHOD || next_iter->first.method == stat_key.method))
192       continue;
193 
194     if (!sent && !received && !sent_blocking)
195         continue;  // No stats collected for this line, skip it and continue.
196 
197     // Add a line to the result and clear the counts.
198     std::string line;
199     if (show == SHOW_SERVICE) {
200       line += stat_key.service;
201     } else {
202       // The interface usually includes the service so don't show both.
203       line += stat_key.interface;
204       if (show >= SHOW_METHOD)
205         line += "." + stat_key.method;
206     }
207     line += base::StringPrintf(":");
208     if (sent_blocking) {
209       line += base::StringPrintf(" Sent (BLOCKING):");
210       if (format == FORMAT_TOTALS)
211         line += base::StringPrintf(" %d", sent_blocking);
212       else if (format == FORMAT_PER_MINUTE)
213         line += base::StringPrintf(" %d/min", sent_blocking / dminutes);
214       else if (format == FORMAT_ALL)
215         line += base::StringPrintf(" %d (%d/min)",
216                                    sent_blocking, sent_blocking / dminutes);
217     }
218     if (sent) {
219       line += base::StringPrintf(" Sent:");
220       if (format == FORMAT_TOTALS)
221         line += base::StringPrintf(" %d", sent);
222       else if (format == FORMAT_PER_MINUTE)
223         line += base::StringPrintf(" %d/min", sent / dminutes);
224       else if (format == FORMAT_ALL)
225         line += base::StringPrintf(" %d (%d/min)", sent, sent / dminutes);
226     }
227     if (received) {
228       line += base::StringPrintf(" Received:");
229       if (format == FORMAT_TOTALS)
230         line += base::StringPrintf(" %d", received);
231       else if (format == FORMAT_PER_MINUTE)
232         line += base::StringPrintf(" %d/min", received / dminutes);
233       else if (format == FORMAT_ALL)
234         line += base::StringPrintf(
235             " %d (%d/min)", received, received / dminutes);
236     }
237     result += line + "\n";
238     sent = 0;
239     sent_blocking = 0;
240     received = 0;
241   }
242   return result;
243 }
244 
245 namespace testing {
246 
GetCalls(const std::string & service,const std::string & interface,const std::string & method,int * sent,int * received,int * blocking)247 bool GetCalls(const std::string& service,
248               const std::string& interface,
249               const std::string& method,
250               int* sent,
251               int* received,
252               int* blocking) {
253   if (!g_dbus_statistics)
254     return false;
255   StatValue* stat =
256       g_dbus_statistics->GetStats(service, interface, method, false);
257   if (!stat)
258     return false;
259   *sent = stat->sent_method_calls;
260   *received = stat->received_signals;
261   *blocking = stat->sent_blocking_method_calls;
262   return true;
263 }
264 
265 }  // namespace testing
266 
267 }  // namespace statistics
268 }  // namespace dbus
269