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.
39struct 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
64// Statistic counters for the entire tool, including aggregated
65// ThreadLocalStats (which are made private so that one can not
66// increment them using the global stats object).
67struct Stats : private ThreadLocalStats {
68  Stats() {
69    memset(this, 0, sizeof(*this));
70    ANNOTATE_BENIGN_RACE(&vts_clone, "Race on vts_clone");
71    ANNOTATE_BENIGN_RACE(&ignore_below_cache_miss,
72                         "Race on ignore_below_cache_miss");
73    ANNOTATE_BENIGN_RACE_SIZED(msm_branch_count, sizeof(msm_branch_count),
74                               "Race on msm_branch_count[]");
75  }
76
77  void Add(const ThreadLocalStats &s) {
78    uintptr_t *p1 = (uintptr_t*)this;
79    uintptr_t *p2 = (uintptr_t*)&s;
80    size_t n = sizeof(s) / sizeof(uintptr_t);
81    for (size_t i = 0; i < n; i++) {
82      p1[i] += p2[i];
83    }
84  }
85
86  void PrintStats() {
87    PrintEventStats();
88    Printf("   VTS: created small/big: %'ld / %'ld; "
89           "deleted small/big: %'ld / %'ld; cloned: %'ld\n",
90           vts_create_small, vts_create_big,
91           vts_delete_small, vts_delete_big, vts_clone);
92    Printf("   vts_total_create  = %'ld; avg=%'ld; delete = %'ld\n",
93           vts_total_create,
94           vts_total_create / (vts_create_small + vts_create_big + 1),
95           vts_total_delete);
96    Printf("   n_seg_hb        = %'ld\n", n_seg_hb);
97    Printf("   n_vts_hb        = %'ld\n", n_vts_hb);
98    Printf("   n_vts_hb_cached = %'ld\n", n_vts_hb_cached);
99    Printf("   memory access:\n"
100           "     1: %'ld / %'ld\n"
101           "     2: %'ld / %'ld\n"
102           "     4: %'ld / %'ld\n"
103           "     8: %'ld / %'ld\n"
104           "     s: %'ld\n",
105           n_fast_access1, n_slow_access1,
106           n_fast_access2, n_slow_access2,
107           n_fast_access4, n_slow_access4,
108           n_fast_access8, n_slow_access8,
109           n_very_slow_access);
110    PrintStatsForCache();
111//    Printf("   Mops:\n"
112//           "    total  = %'ld\n"
113//           "    unique = %'ld\n",
114//           mops_total, mops_uniq);
115    Printf("   Publish: set: %'ld; get: %'ld; clear: %'ld\n",
116           publish_set, publish_get, publish_clear);
117
118    Printf("   PcTo: all: %'ld\n", pc_to_strings);
119
120    Printf("   StackTrace: create: %'ld; delete %'ld\n",
121           stack_trace_create, stack_trace_delete);
122
123    Printf("   History segments: same: %'ld; reuse: %'ld; "
124           "preallocated: %'ld; new: %'ld\n",
125           history_uses_same_segment, history_reuses_segment,
126           history_uses_preallocated_segment, history_creates_new_segment);
127    Printf("   Forget all history: %'ld\n", n_forgets);
128
129    PrintStatsForSeg();
130    PrintStatsForSS();
131    PrintStatsForLS();
132  }
133
134  void PrintStatsForSS() {
135    Printf("   SegmentSet: created: %'ld; reused: %'ld;"
136           " find: %'ld; recycle: %'ld\n",
137           ss_create, ss_reuse, ss_find, ss_recycle);
138    Printf("        sizes: 2: %'ld; 3: %'ld; 4: %'ld; other: %'ld\n",
139           ss_size_2, ss_size_3, ss_size_4, ss_size_other);
140
141    // SSEq is called at least (ss_find + ss_recycle) times since
142    // FindExistingOrAlocateAndCopy calls map_.find()
143    // and RecycleOneSegmentSet calls map_.erase(it)
144    // Both find() and erase(it) require at least one call to SSHash and SSEq.
145    //
146    // Apart from SSHash call locations mentioned above,
147    // SSHash is called for each AllocateAndCopy (ss_create + ss_reuse) times
148    // for insert() AFTER it has already been called
149    // by FindExistingOrAlocateAndCopy in case find() returned map_.end().
150    // Hence the factor of 2.
151    uintptr_t sseq_estimated = ss_find + ss_recycle,
152            sshash_estimated = sseq_estimated + 2 * (ss_create + ss_reuse);
153    Printf("   SSHash called %12ld times (vs. %12ld = +%d%%)\n"
154           "   SSEq   called %12ld times (vs. %12ld = +%d%%)\n",
155            sshash_calls, sshash_estimated,
156            (sshash_calls - sshash_estimated)/(sshash_estimated/100 + 1),
157            sseq_calls,   sseq_estimated,
158            (sseq_calls   - sseq_estimated  )/(sseq_estimated/100 + 1));
159  }
160  void PrintStatsForCache() {
161    Printf("   Cache:\n"
162           "    new       = %'ld\n"
163           "    delete    = %'ld\n"
164           "    fetch     = %'ld\n"
165           "    storage   = %'ld\n",
166           cache_new_line,
167           cache_delete_empty_line, cache_fetch,
168           cache_max_storage_size);
169  }
170
171  void PrintStatsForSeg() {
172    Printf("   Segment: created: %'ld; reused: %'ld\n",
173           seg_create, seg_reuse);
174  }
175
176  void PrintStatsForLS() {
177    Printf("   LockSet add: 0: %'ld; 1 : %'ld; n : %'ld\n",
178           ls_add_to_empty, ls_add_to_singleton, ls_add_to_multi);
179    Printf("   LockSet rem: 1: %'ld; n : %'ld\n",
180           ls_remove_from_singleton, ls_remove_from_multi);
181    Printf("   LockSet cache: add : %'ld; rem : %'ld; fast: %'ld\n",
182           ls_add_cache_hit, ls_rem_cache_hit, ls_cache_fast);
183    Printf("   LockSet size: 2: %'ld 3: %'ld 4: %'ld 5: %'ld other: %'ld\n",
184           ls_size_2, ls_size_3, ls_size_4, ls_size_5, ls_size_other);
185  }
186
187  void PrintEventStats() {
188    uintptr_t total = 0;
189    for (int i = 0; i < LAST_EVENT; i++) {
190      if (events[i]) {
191        Printf("  %25s: %'ld\n", Event::TypeString((EventType)i),
192               events[i]);
193      }
194      total += events[i];
195    }
196    Printf("  %25s: %'ld\n", "Total", total);
197    for (size_t i = 0; i < TS_ARRAY_SIZE(memory_access_sizes); i++) {
198      if (memory_access_sizes[i]) {
199        Printf("  mop[%d]: %'ld\n", i, memory_access_sizes[i]);
200      }
201    }
202    for (size_t i = 0; i < TS_ARRAY_SIZE(mops_per_trace); i++) {
203      Printf("  mops_per_trace[%d] = %'ld\n", i, mops_per_trace[i]);
204    }
205    for (size_t i = 0; i < TS_ARRAY_SIZE(locks_per_trace); i++) {
206      Printf("  locks_per_trace[%d] = %'ld\n", i, locks_per_trace[i]);
207    }
208
209    uintptr_t total_locks = 0;
210    for (size_t i = 0; i < TS_ARRAY_SIZE(lock_sites); i++) {
211      if(lock_sites[i] == 0) continue;
212      Printf("lock_sites[%ld]=%ld\n", i, lock_sites[i]);
213      total_locks += lock_sites[i];
214    }
215    Printf("lock_sites[*]=%ld\n", total_locks);
216    Printf("futex_wait   =%ld\n", futex_wait);
217    Printf("unlocked_access_ok =%'ld\n", unlocked_access_ok);
218    uintptr_t all_locked_access = 0;
219    for (size_t i = 0; i < TS_ARRAY_SIZE(locked_access); i++) {
220      uintptr_t t = locked_access[i];
221      if (t) Printf("locked_access[%ld]   =%'ld\n", i, t);
222      all_locked_access += t;
223    }
224    Printf("locked_access[*]   =%'ld\n", all_locked_access);
225    Printf("try_acquire_line_spin =%ld\n", try_acquire_line_spin);
226    Printf("access to first 1/2/4 G: %'ld %'ld %'ld\n",
227           access_to_first_1g, access_to_first_2g, access_to_first_4g);
228
229
230    for (size_t i = 0; i < TS_ARRAY_SIZE(tleb_flush); i++) {
231      if(tleb_flush[i] == 0) continue;
232      Printf("tleb_flush[%ld]=%ld\n", i, tleb_flush[i]);
233    }
234    Printf("IgnoreBelowCache miss=%ld\n", ignore_below_cache_miss);
235    for (size_t i = 0; i < TS_ARRAY_SIZE(msm_branch_count); i++) {
236      if (msm_branch_count[i])
237        Printf("msm_branch_count[%02d] = %'ld\n", i, msm_branch_count[i]);
238    }
239    if (read_proc_self_stats)
240      Printf("read_proc_self_stats   =%ld\n", read_proc_self_stats);
241  }
242
243
244
245  uintptr_t n_vts_hb;
246  uintptr_t n_vts_hb_cached;
247  uintptr_t n_seg_hb;
248
249  uintptr_t ls_add_to_empty, ls_add_to_singleton, ls_add_to_multi,
250            ls_remove_from_singleton, ls_remove_from_multi,
251            ls_add_cache_hit, ls_rem_cache_hit,
252            ls_cache_fast,
253            ls_size_2, ls_size_3, ls_size_4, ls_size_5, ls_size_other;
254
255  uintptr_t cache_new_line;
256  uintptr_t cache_delete_empty_line;
257  uintptr_t cache_fetch;
258  uintptr_t cache_max_storage_size;
259
260  uintptr_t mops_total;
261  uintptr_t mops_uniq;
262
263  uintptr_t vts_create_big, vts_create_small,
264            vts_clone, vts_delete_small, vts_delete_big,
265            vts_total_delete, vts_total_create;
266
267  uintptr_t ss_create, ss_reuse, ss_find, ss_recycle;
268  uintptr_t ss_size_2, ss_size_3, ss_size_4, ss_size_other;
269
270  uintptr_t sshash_calls, sseq_calls;
271
272  uintptr_t seg_create, seg_reuse;
273
274  uintptr_t publish_set, publish_get, publish_clear;
275
276  uintptr_t pc_to_strings;
277
278  uintptr_t stack_trace_create, stack_trace_delete;
279
280  uintptr_t n_forgets;
281
282  uintptr_t lock_sites[20];
283
284  uintptr_t tleb_flush[10];
285
286  uintptr_t ignore_below_cache_miss;
287
288  uintptr_t try_acquire_line_spin;
289  uintptr_t futex_wait;
290  uintptr_t read_proc_self_stats;
291};
292
293
294// end. {{{1
295#endif  // TS_STATS_
296// vim:shiftwidth=2:softtabstop=2:expandtab:tw=80
297