Home | History | Annotate | Download | only in dbus
      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