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