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 <set>
      8 
      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/time.h"
     15 
     16 namespace dbus {
     17 
     18 namespace {
     19 
     20 // Used to store dbus statistics sorted alphabetically by service, interface,
     21 // then method (using std::string <).
     22 struct Stat {
     23   Stat(const std::string& service,
     24        const std::string& interface,
     25        const std::string& method)
     26       : service(service),
     27         interface(interface),
     28         method(method),
     29         sent_method_calls(0),
     30         received_signals(0),
     31         sent_blocking_method_calls(0) {
     32   }
     33   std::string service;
     34   std::string interface;
     35   std::string method;
     36   int sent_method_calls;
     37   int received_signals;
     38   int sent_blocking_method_calls;
     39 
     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;
     46   }
     47 
     48   struct PtrCompare {
     49     bool operator()(Stat* lhs, Stat* rhs) const {
     50       DCHECK(lhs && rhs);
     51       return lhs->Compare(*rhs);
     52     }
     53   };
     54 };
     55 
     56 typedef std::set<Stat*, Stat::PtrCompare> StatSet;
     57 
     58 //------------------------------------------------------------------------------
     59 // DBusStatistics
     60 
     61 // Simple class for gathering DBus usage statistics.
     62 class DBusStatistics {
     63  public:
     64   DBusStatistics()
     65       : start_time_(base::Time::Now()),
     66         origin_thread_id_(base::PlatformThread::CurrentId()) {
     67   }
     68 
     69   ~DBusStatistics() {
     70     DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
     71     STLDeleteContainerPointers(stats_.begin(), stats_.end());
     72   }
     73 
     74   // Enum to specify which field in Stat to increment in AddStat
     75   enum StatType {
     76     TYPE_SENT_METHOD_CALLS,
     77     TYPE_RECEIVED_SIGNALS,
     78     TYPE_SENT_BLOCKING_METHOD_CALLS
     79   };
     80 
     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,
     85                StatType type) {
     86     if (base::PlatformThread::CurrentId() != origin_thread_id_) {
     87       DVLOG(1) << "Ignoring DBusStatistics::AddStat call from thread: "
     88                << base::PlatformThread::CurrentId();
     89       return;
     90     }
     91     Stat* stat = GetStat(service, interface, method, true);
     92     DCHECK(stat);
     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;
     99     else
    100       NOTREACHED();
    101   }
    102 
    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,
    108                 bool add_stat) {
    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())
    113       return *found;
    114     if (!add_stat)
    115       return NULL;
    116     found = stats_.insert(stat.release()).first;
    117     return *found;
    118   }
    119 
    120   StatSet& stats() { return stats_; }
    121   base::Time start_time() { return start_time_; }
    122 
    123  private:
    124   StatSet stats_;
    125   base::Time start_time_;
    126   base::PlatformThreadId origin_thread_id_;
    127 
    128   DISALLOW_COPY_AND_ASSIGN(DBusStatistics);
    129 };
    130 
    131 DBusStatistics* g_dbus_statistics = NULL;
    132 
    133 }  // namespace
    134 
    135 //------------------------------------------------------------------------------
    136 
    137 namespace statistics {
    138 
    139 void Initialize() {
    140   if (g_dbus_statistics)
    141     delete g_dbus_statistics;  // reset statistics
    142   g_dbus_statistics = new DBusStatistics();
    143 }
    144 
    145 void Shutdown() {
    146   delete g_dbus_statistics;
    147   g_dbus_statistics = NULL;
    148 }
    149 
    150 void AddSentMethodCall(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, DBusStatistics::TYPE_SENT_METHOD_CALLS);
    157 }
    158 
    159 void AddReceivedSignal(const std::string& service,
    160                        const std::string& interface,
    161                        const std::string& method) {
    162   if (!g_dbus_statistics)
    163     return;
    164   g_dbus_statistics->AddStat(
    165       service, interface, method, DBusStatistics::TYPE_RECEIVED_SIGNALS);
    166 }
    167 
    168 void AddBlockingSentMethodCall(const std::string& service,
    169                                const std::string& interface,
    170                                const std::string& method) {
    171   if (!g_dbus_statistics)
    172     return;
    173   g_dbus_statistics->AddStat(
    174       service, interface, method,
    175       DBusStatistics::TYPE_SENT_BLOCKING_METHOD_CALLS);
    176 }
    177 
    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.";
    183 
    184   const StatSet& stats = g_dbus_statistics->stats();
    185   if (stats.empty())
    186     return "No DBus calls.";
    187 
    188   base::TimeDelta dtime = base::Time::Now() - g_dbus_statistics->start_time();
    189   int dminutes = dtime.InMinutes();
    190   dminutes = std::max(dminutes, 1);
    191 
    192   std::string result;
    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
    203     // stat, continue.
    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))
    208       continue;
    209 
    210     if (!sent && !received && !sent_blocking)
    211         continue;  // No stats collected for this line, skip it and continue.
    212 
    213     // Add a line to the result and clear the counts.
    214     std::string line;
    215     if (show == SHOW_SERVICE) {
    216       line += stat->service;
    217     } else {
    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;
    222     }
    223     line += base::StringPrintf(":");
    224     if (sent_blocking) {
    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);
    233     }
    234     if (sent) {
    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);
    242     }
    243     if (received) {
    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);
    252     }
    253     result += line + "\n";
    254     sent = 0;
    255     sent_blocking = 0;
    256     received = 0;
    257   }
    258   return result;
    259 }
    260 
    261 namespace testing {
    262 
    263 bool GetCalls(const std::string& service,
    264               const std::string& interface,
    265               const std::string& method,
    266               int* sent,
    267               int* received,
    268               int* blocking) {
    269   if (!g_dbus_statistics)
    270     return false;
    271   Stat* stat = g_dbus_statistics->GetStat(service, interface, method, false);
    272   if (!stat)
    273     return false;
    274   *sent = stat->sent_method_calls;
    275   *received = stat->received_signals;
    276   *blocking = stat->sent_blocking_method_calls;
    277   return true;
    278 }
    279 
    280 }  // namespace testing
    281 
    282 }  // namespace statistics
    283 }  // namespace dbus
    284