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 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: 45 DBusStatistics() 46 : start_time_(base::Time::Now()), 47 origin_thread_id_(base::PlatformThread::CurrentId()) { 48 } 49 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. 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. 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 102 StatMap& stats() { return stats_; } 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 121 void Initialize() { 122 if (g_dbus_statistics) 123 delete g_dbus_statistics; // reset statistics 124 g_dbus_statistics = new DBusStatistics(); 125 } 126 127 void Shutdown() { 128 delete g_dbus_statistics; 129 g_dbus_statistics = nullptr; 130 } 131 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 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 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. 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 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