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/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