Home | History | Annotate | Download | only in tsan
      1 /* Copyright (c) 2008-2010, Google Inc.
      2  * All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Neither the name of Google Inc. nor the names of its
     11  * contributors may be used to endorse or promote products derived from
     12  * this software without specific prior written permission.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 // This file is part of ThreadSanitizer, a dynamic data race detector.
     28 // Author: Konstantin Serebryany.
     29 // Author: Timur Iskhodzhanov.
     30 #ifndef TS_STATS_
     31 #define TS_STATS_
     32 
     33 #include "dynamic_annotations.h"
     34 #include "ts_util.h"
     35 
     36 // Statistic counters for each thread.
     37 // For stats accessed concurrently from different threads
     38 // we don't want to use global stats to avoid cache line ping-pong.
     39 struct ThreadLocalStats {
     40   ThreadLocalStats() { Clear(); }
     41   void Clear() {
     42     memset(this, 0, sizeof(*this));
     43   }
     44   uintptr_t memory_access_sizes[18];
     45   uintptr_t events[LAST_EVENT];
     46   uintptr_t unlocked_access_ok;
     47   uintptr_t n_fast_access1, n_fast_access2, n_fast_access4, n_fast_access8,
     48             n_slow_access1, n_slow_access2, n_slow_access4, n_slow_access8,
     49             n_very_slow_access, n_access_slow_iter;
     50 
     51   uintptr_t mops_per_trace[16];
     52   uintptr_t locks_per_trace[16];
     53   uintptr_t locked_access[8];
     54   uintptr_t history_uses_same_segment, history_creates_new_segment,
     55             history_reuses_segment, history_uses_preallocated_segment;
     56 
     57   uintptr_t msm_branch_count[16];
     58 
     59   uintptr_t access_to_first_1g;
     60   uintptr_t access_to_first_2g;
     61   uintptr_t access_to_first_4g;
     62 
     63   uintptr_t cache_fast_get;
     64 };
     65 
     66 // Statistic counters for the entire tool, including aggregated
     67 // ThreadLocalStats (which are made private so that one can not
     68 // increment them using the global stats object).
     69 struct Stats : private ThreadLocalStats {
     70   Stats() {
     71     memset(this, 0, sizeof(*this));
     72     ANNOTATE_BENIGN_RACE(&vts_clone, "Race on vts_clone");
     73     ANNOTATE_BENIGN_RACE(&ignore_below_cache_miss,
     74                          "Race on ignore_below_cache_miss");
     75     ANNOTATE_BENIGN_RACE_SIZED(msm_branch_count, sizeof(msm_branch_count),
     76                                "Race on msm_branch_count[]");
     77   }
     78 
     79   void Add(const ThreadLocalStats &s) {
     80     uintptr_t *p1 = (uintptr_t*)this;
     81     uintptr_t *p2 = (uintptr_t*)&s;
     82     size_t n = sizeof(s) / sizeof(uintptr_t);
     83     for (size_t i = 0; i < n; i++) {
     84       p1[i] += p2[i];
     85     }
     86   }
     87 
     88   void PrintStats() {
     89     PrintEventStats();
     90     Printf("   VTS: created small/big: %'ld / %'ld; "
     91            "deleted small/big: %'ld / %'ld; cloned: %'ld\n",
     92            vts_create_small, vts_create_big,
     93            vts_delete_small, vts_delete_big, vts_clone);
     94     Printf("   vts_total_create  = %'ld; avg=%'ld; delete = %'ld\n",
     95            vts_total_create,
     96            vts_total_create / (vts_create_small + vts_create_big + 1),
     97            vts_total_delete);
     98     Printf("   n_seg_hb        = %'ld\n", n_seg_hb);
     99     Printf("   n_vts_hb        = %'ld\n", n_vts_hb);
    100     Printf("   n_vts_hb_cached = %'ld\n", n_vts_hb_cached);
    101     Printf("   memory access:\n"
    102            "     1: %'ld / %'ld\n"
    103            "     2: %'ld / %'ld\n"
    104            "     4: %'ld / %'ld\n"
    105            "     8: %'ld / %'ld\n"
    106            "     s: %'ld\n",
    107            n_fast_access1, n_slow_access1,
    108            n_fast_access2, n_slow_access2,
    109            n_fast_access4, n_slow_access4,
    110            n_fast_access8, n_slow_access8,
    111            n_very_slow_access);
    112     PrintStatsForCache();
    113 //    Printf("   Mops:\n"
    114 //           "    total  = %'ld\n"
    115 //           "    unique = %'ld\n",
    116 //           mops_total, mops_uniq);
    117     Printf("   Publish: set: %'ld; get: %'ld; clear: %'ld\n",
    118            publish_set, publish_get, publish_clear);
    119 
    120     Printf("   PcTo: all: %'ld\n", pc_to_strings);
    121 
    122     Printf("   StackTrace: create: %'ld; delete %'ld\n",
    123            stack_trace_create, stack_trace_delete);
    124 
    125     Printf("   History segments: same: %'ld; reuse: %'ld; "
    126            "preallocated: %'ld; new: %'ld\n",
    127            history_uses_same_segment, history_reuses_segment,
    128            history_uses_preallocated_segment, history_creates_new_segment);
    129     Printf("   Forget all history: %'ld\n", n_forgets);
    130 
    131     PrintStatsForSeg();
    132     PrintStatsForSS();
    133     PrintStatsForLS();
    134   }
    135 
    136   void PrintStatsForSS() {
    137     Printf("   SegmentSet: created: %'ld; reused: %'ld;"
    138            " find: %'ld; recycle: %'ld\n",
    139            ss_create, ss_reuse, ss_find, ss_recycle);
    140     Printf("        sizes: 2: %'ld; 3: %'ld; 4: %'ld; other: %'ld\n",
    141            ss_size_2, ss_size_3, ss_size_4, ss_size_other);
    142 
    143     // SSEq is called at least (ss_find + ss_recycle) times since
    144     // FindExistingOrAlocateAndCopy calls map_.find()
    145     // and RecycleOneSegmentSet calls map_.erase(it)
    146     // Both find() and erase(it) require at least one call to SSHash and SSEq.
    147     //
    148     // Apart from SSHash call locations mentioned above,
    149     // SSHash is called for each AllocateAndCopy (ss_create + ss_reuse) times
    150     // for insert() AFTER it has already been called
    151     // by FindExistingOrAlocateAndCopy in case find() returned map_.end().
    152     // Hence the factor of 2.
    153     uintptr_t sseq_estimated = ss_find + ss_recycle,
    154             sshash_estimated = sseq_estimated + 2 * (ss_create + ss_reuse);
    155     Printf("   SSHash called %12ld times (vs. %12ld = +%d%%)\n"
    156            "   SSEq   called %12ld times (vs. %12ld = +%d%%)\n",
    157             sshash_calls, sshash_estimated,
    158             (sshash_calls - sshash_estimated)/(sshash_estimated/100 + 1),
    159             sseq_calls,   sseq_estimated,
    160             (sseq_calls   - sseq_estimated  )/(sseq_estimated/100 + 1));
    161   }
    162   void PrintStatsForCache() {
    163     Printf("   Cache:\n"
    164            "    fast      = %'ld\n"
    165            "    new       = %'ld\n"
    166            "    delete    = %'ld\n"
    167            "    fetch     = %'ld\n"
    168            "    storage   = %'ld\n",
    169            cache_fast_get, cache_new_line,
    170            cache_delete_empty_line, cache_fetch,
    171            cache_max_storage_size);
    172   }
    173 
    174   void PrintStatsForSeg() {
    175     Printf("   Segment: created: %'ld; reused: %'ld\n",
    176            seg_create, seg_reuse);
    177   }
    178 
    179   void PrintStatsForLS() {
    180     Printf("   LockSet add: 0: %'ld; 1 : %'ld; n : %'ld\n",
    181            ls_add_to_empty, ls_add_to_singleton, ls_add_to_multi);
    182     Printf("   LockSet rem: 1: %'ld; n : %'ld\n",
    183            ls_remove_from_singleton, ls_remove_from_multi);
    184     Printf("   LockSet cache: add : %'ld; rem : %'ld; fast: %'ld\n",
    185            ls_add_cache_hit, ls_rem_cache_hit, ls_cache_fast);
    186     Printf("   LockSet size: 2: %'ld 3: %'ld 4: %'ld 5: %'ld other: %'ld\n",
    187            ls_size_2, ls_size_3, ls_size_4, ls_size_5, ls_size_other);
    188   }
    189 
    190   void PrintEventStats() {
    191     uintptr_t total = 0;
    192     for (int i = 0; i < LAST_EVENT; i++) {
    193       if (events[i]) {
    194         Printf("  %25s: %'ld\n", Event::TypeString((EventType)i),
    195                events[i]);
    196       }
    197       total += events[i];
    198     }
    199     Printf("  %25s: %'ld\n", "Total", total);
    200     for (size_t i = 0; i < TS_ARRAY_SIZE(memory_access_sizes); i++) {
    201       if (memory_access_sizes[i]) {
    202         Printf("  mop[%d]: %'ld\n", i, memory_access_sizes[i]);
    203       }
    204     }
    205     for (size_t i = 0; i < TS_ARRAY_SIZE(mops_per_trace); i++) {
    206       Printf("  mops_per_trace[%d] = %'ld\n", i, mops_per_trace[i]);
    207     }
    208     for (size_t i = 0; i < TS_ARRAY_SIZE(locks_per_trace); i++) {
    209       Printf("  locks_per_trace[%d] = %'ld\n", i, locks_per_trace[i]);
    210     }
    211 
    212     uintptr_t total_locks = 0;
    213     for (size_t i = 0; i < TS_ARRAY_SIZE(lock_sites); i++) {
    214       if(lock_sites[i] == 0) continue;
    215       Printf("lock_sites[%ld]=%ld\n", i, lock_sites[i]);
    216       total_locks += lock_sites[i];
    217     }
    218     Printf("lock_sites[*]=%ld\n", total_locks);
    219     Printf("futex_wait   =%ld\n", futex_wait);
    220     Printf("unlocked_access_ok =%'ld\n", unlocked_access_ok);
    221     uintptr_t all_locked_access = 0;
    222     for (size_t i = 0; i < TS_ARRAY_SIZE(locked_access); i++) {
    223       uintptr_t t = locked_access[i];
    224       if (t) Printf("locked_access[%ld]   =%'ld\n", i, t);
    225       all_locked_access += t;
    226     }
    227     Printf("locked_access[*]   =%'ld\n", all_locked_access);
    228     Printf("try_acquire_line_spin =%ld\n", try_acquire_line_spin);
    229     Printf("access to first 1/2/4 G: %'ld %'ld %'ld\n",
    230            access_to_first_1g, access_to_first_2g, access_to_first_4g);
    231 
    232 
    233     for (size_t i = 0; i < TS_ARRAY_SIZE(tleb_flush); i++) {
    234       if(tleb_flush[i] == 0) continue;
    235       Printf("tleb_flush[%ld]=%ld\n", i, tleb_flush[i]);
    236     }
    237     Printf("IgnoreBelowCache miss=%ld\n", ignore_below_cache_miss);
    238     for (size_t i = 0; i < TS_ARRAY_SIZE(msm_branch_count); i++) {
    239       if (msm_branch_count[i])
    240         Printf("msm_branch_count[%02d] = %'ld\n", i, msm_branch_count[i]);
    241     }
    242     if (read_proc_self_stats)
    243       Printf("read_proc_self_stats   =%ld\n", read_proc_self_stats);
    244   }
    245 
    246 
    247 
    248   uintptr_t n_vts_hb;
    249   uintptr_t n_vts_hb_cached;
    250   uintptr_t n_seg_hb;
    251 
    252   uintptr_t ls_add_to_empty, ls_add_to_singleton, ls_add_to_multi,
    253             ls_remove_from_singleton, ls_remove_from_multi,
    254             ls_add_cache_hit, ls_rem_cache_hit,
    255             ls_cache_fast,
    256             ls_size_2, ls_size_3, ls_size_4, ls_size_5, ls_size_other;
    257 
    258   uintptr_t cache_new_line;
    259   uintptr_t cache_delete_empty_line;
    260   uintptr_t cache_fetch;
    261   uintptr_t cache_max_storage_size;
    262 
    263   uintptr_t mops_total;
    264   uintptr_t mops_uniq;
    265 
    266   uintptr_t vts_create_big, vts_create_small,
    267             vts_clone, vts_delete_small, vts_delete_big,
    268             vts_total_delete, vts_total_create;
    269 
    270   uintptr_t ss_create, ss_reuse, ss_find, ss_recycle;
    271   uintptr_t ss_size_2, ss_size_3, ss_size_4, ss_size_other;
    272 
    273   uintptr_t sshash_calls, sseq_calls;
    274 
    275   uintptr_t seg_create, seg_reuse;
    276 
    277   uintptr_t publish_set, publish_get, publish_clear;
    278 
    279   uintptr_t pc_to_strings;
    280 
    281   uintptr_t stack_trace_create, stack_trace_delete;
    282 
    283   uintptr_t n_forgets;
    284 
    285   uintptr_t lock_sites[20];
    286 
    287   uintptr_t tleb_flush[10];
    288 
    289   uintptr_t ignore_below_cache_miss;
    290 
    291   uintptr_t try_acquire_line_spin;
    292   uintptr_t futex_wait;
    293   uintptr_t read_proc_self_stats;
    294 };
    295 
    296 
    297 // end. {{{1
    298 #endif  // TS_STATS_
    299 // vim:shiftwidth=2:softtabstop=2:expandtab:tw=80
    300