blob: fafac5f80c307b0d6c7e4ae15c7d1c8fa3108f08 [file] [log] [blame]
sewardjaf44c822007-11-25 14:01:38 +00001/*
2 This file is part of drd, a data race detector.
3
sewardj85642922008-01-14 11:54:56 +00004 Copyright (C) 2006-2008 Bart Van Assche
sewardjaf44c822007-11-25 14:01:38 +00005 bart.vanassche@gmail.com
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307, USA.
21
22 The GNU General Public License is contained in the file COPYING.
23*/
24
25
26#include "drd_error.h"
27#include "drd_segment.h"
28#include "drd_suppression.h"
29#include "drd_thread.h"
sewardjaf44c822007-11-25 14:01:38 +000030#include "pub_tool_basics.h" // Addr, SizeT
31#include "pub_tool_errormgr.h" // VG_(unique_error)()
32#include "pub_tool_libcassert.h" // tl_assert()
33#include "pub_tool_libcbase.h" // VG_(strlen)()
34#include "pub_tool_libcprint.h" // VG_(printf)()
35#include "pub_tool_machine.h"
36#include "pub_tool_mallocfree.h" // VG_(malloc)(), VG_(free)()
sewardj85642922008-01-14 11:54:56 +000037#include "pub_tool_options.h" // VG_(clo_backtrace_size)
sewardjaf44c822007-11-25 14:01:38 +000038#include "pub_tool_threadstate.h" // VG_(get_pthread_id)()
39
40
sewardjaf44c822007-11-25 14:01:38 +000041// Local functions.
42
43static void thread_append_segment(const DrdThreadId tid,
44 Segment* const sg);
45static void thread_update_danger_set(const DrdThreadId tid);
46
47
48// Local variables.
49
50static ULong s_context_switch_count;
51static ULong s_discard_ordered_segments_count;
sewardjaf44c822007-11-25 14:01:38 +000052static ULong s_update_danger_set_count;
53static ULong s_danger_set_bitmap_creation_count;
54static ULong s_danger_set_bitmap2_creation_count;
sewardj8b09d4f2007-12-04 21:27:18 +000055static ThreadId s_vg_running_tid = VG_INVALID_THREADID;
bartf00a85b2008-03-13 18:49:23 +000056DrdThreadId s_drd_running_tid = DRD_INVALID_THREADID;
57ThreadInfo s_threadinfo[DRD_N_THREADS];
bart1a473c72008-03-13 19:03:38 +000058struct bitmap* s_danger_set;
bart26f73e12008-02-24 18:37:08 +000059static Bool s_trace_context_switches = False;
60static Bool s_trace_danger_set = False;
sewardjaf44c822007-11-25 14:01:38 +000061
62
63// Function definitions.
64
bart26f73e12008-02-24 18:37:08 +000065void thread_trace_context_switches(const Bool t)
66{
bart3772a982008-03-15 08:11:03 +000067 s_trace_context_switches = t;
bart26f73e12008-02-24 18:37:08 +000068}
69
70void thread_trace_danger_set(const Bool t)
71{
bart3772a982008-03-15 08:11:03 +000072 s_trace_danger_set = t;
bart26f73e12008-02-24 18:37:08 +000073}
74
sewardjaf44c822007-11-25 14:01:38 +000075__inline__ Bool IsValidDrdThreadId(const DrdThreadId tid)
76{
bart3772a982008-03-15 08:11:03 +000077 return (0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID
78 && ! (s_threadinfo[tid].vg_thread_exists == False
79 && s_threadinfo[tid].posix_thread_exists == False
80 && s_threadinfo[tid].detached_posix_thread == False));
sewardjaf44c822007-11-25 14:01:38 +000081}
82
83/**
84 * Convert Valgrind's ThreadId into a DrdThreadId. Report failure if
85 * Valgrind's ThreadId does not yet exist.
86 **/
87DrdThreadId VgThreadIdToDrdThreadId(const ThreadId tid)
88{
bart3772a982008-03-15 08:11:03 +000089 int i;
sewardjaf44c822007-11-25 14:01:38 +000090
bart3772a982008-03-15 08:11:03 +000091 if (tid == VG_INVALID_THREADID)
92 return DRD_INVALID_THREADID;
sewardjaf44c822007-11-25 14:01:38 +000093
bart3772a982008-03-15 08:11:03 +000094 for (i = 1; i < DRD_N_THREADS; i++)
95 {
96 if (s_threadinfo[i].vg_thread_exists == True
97 && s_threadinfo[i].vg_threadid == tid)
98 {
99 return i;
100 }
101 }
sewardjaf44c822007-11-25 14:01:38 +0000102
bart3772a982008-03-15 08:11:03 +0000103 return DRD_INVALID_THREADID;
sewardjaf44c822007-11-25 14:01:38 +0000104}
105
106static
107DrdThreadId VgThreadIdToNewDrdThreadId(const ThreadId tid)
108{
bart3772a982008-03-15 08:11:03 +0000109 int i;
sewardjaf44c822007-11-25 14:01:38 +0000110
bart3772a982008-03-15 08:11:03 +0000111 tl_assert(VgThreadIdToDrdThreadId(tid) == DRD_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000112
bart3772a982008-03-15 08:11:03 +0000113 for (i = 1; i < DRD_N_THREADS; i++)
114 {
115 if (s_threadinfo[i].vg_thread_exists == False
116 && s_threadinfo[i].posix_thread_exists == False
117 && s_threadinfo[i].detached_posix_thread == False)
118 {
119 s_threadinfo[i].vg_thread_exists = True;
120 s_threadinfo[i].vg_threadid = tid;
121 s_threadinfo[i].pt_threadid = INVALID_POSIX_THREADID;
bart3772a982008-03-15 08:11:03 +0000122 s_threadinfo[i].stack_min = 0;
123 s_threadinfo[i].stack_startup = 0;
124 s_threadinfo[i].stack_max = 0;
125 VG_(snprintf)(s_threadinfo[i].name, sizeof(s_threadinfo[i].name),
126 "thread %d", tid);
127 s_threadinfo[i].name[sizeof(s_threadinfo[i].name) - 1] = 0;
128 s_threadinfo[i].is_recording = True;
129 s_threadinfo[i].synchr_nesting = 0;
130 if (s_threadinfo[i].first != 0)
131 VG_(printf)("drd thread id = %d\n", i);
132 tl_assert(s_threadinfo[i].first == 0);
133 tl_assert(s_threadinfo[i].last == 0);
134 return i;
135 }
136 }
sewardjaf44c822007-11-25 14:01:38 +0000137
bart3772a982008-03-15 08:11:03 +0000138 tl_assert(False);
sewardjaf44c822007-11-25 14:01:38 +0000139
bart3772a982008-03-15 08:11:03 +0000140 return DRD_INVALID_THREADID;
sewardjaf44c822007-11-25 14:01:38 +0000141}
142
143DrdThreadId PtThreadIdToDrdThreadId(const PThreadId tid)
144{
bart3772a982008-03-15 08:11:03 +0000145 int i;
sewardjaf44c822007-11-25 14:01:38 +0000146
bart3772a982008-03-15 08:11:03 +0000147 tl_assert(tid != INVALID_POSIX_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000148
bart3772a982008-03-15 08:11:03 +0000149 for (i = 1; i < DRD_N_THREADS; i++)
150 {
151 if (s_threadinfo[i].posix_thread_exists
152 && s_threadinfo[i].pt_threadid == tid)
153 {
154 return i;
155 }
156 }
157 return DRD_INVALID_THREADID;
sewardjaf44c822007-11-25 14:01:38 +0000158}
159
160ThreadId DrdThreadIdToVgThreadId(const DrdThreadId tid)
161{
bart3772a982008-03-15 08:11:03 +0000162 tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID);
163 return (s_threadinfo[tid].vg_thread_exists
164 ? s_threadinfo[tid].vg_threadid
165 : VG_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000166}
167
bart26f73e12008-02-24 18:37:08 +0000168/** Sanity check of the doubly linked list of segments referenced by a
169 * ThreadInfo struct.
170 * @return True if sane, False if not.
sewardjaf44c822007-11-25 14:01:38 +0000171 */
172static Bool sane_ThreadInfo(const ThreadInfo* const ti)
173{
bart3772a982008-03-15 08:11:03 +0000174 Segment* p;
175 for (p = ti->first; p; p = p->next) {
176 if (p->next && p->next->prev != p)
177 return False;
178 if (p->next == 0 && p != ti->last)
179 return False;
180 }
181 for (p = ti->last; p; p = p->prev) {
182 if (p->prev && p->prev->next != p)
183 return False;
184 if (p->prev == 0 && p != ti->first)
185 return False;
186 }
187 return True;
sewardjaf44c822007-11-25 14:01:38 +0000188}
189
190DrdThreadId thread_pre_create(const DrdThreadId creator,
191 const ThreadId vg_created)
192{
bart3772a982008-03-15 08:11:03 +0000193 DrdThreadId created;
sewardjaf44c822007-11-25 14:01:38 +0000194
bart3772a982008-03-15 08:11:03 +0000195 tl_assert(VgThreadIdToDrdThreadId(vg_created) == DRD_INVALID_THREADID);
196 created = VgThreadIdToNewDrdThreadId(vg_created);
197 tl_assert(0 <= created && created < DRD_N_THREADS
198 && created != DRD_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000199
bart3772a982008-03-15 08:11:03 +0000200 tl_assert(s_threadinfo[created].first == 0);
201 tl_assert(s_threadinfo[created].last == 0);
202 thread_append_segment(created, sg_new(creator, created));
sewardjaf44c822007-11-25 14:01:38 +0000203
bart3772a982008-03-15 08:11:03 +0000204 return created;
sewardjaf44c822007-11-25 14:01:38 +0000205}
206
bart26f73e12008-02-24 18:37:08 +0000207/** Allocate the first segment for a thread. Call this just after
208 * pthread_create().
sewardjaf44c822007-11-25 14:01:38 +0000209 */
210DrdThreadId thread_post_create(const ThreadId vg_created)
211{
bart3772a982008-03-15 08:11:03 +0000212 const DrdThreadId created = VgThreadIdToDrdThreadId(vg_created);
sewardjaf44c822007-11-25 14:01:38 +0000213
bart3772a982008-03-15 08:11:03 +0000214 tl_assert(0 <= created && created < DRD_N_THREADS
215 && created != DRD_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000216
bart3772a982008-03-15 08:11:03 +0000217 s_threadinfo[created].stack_max = VG_(thread_get_stack_max)(vg_created);
218 s_threadinfo[created].stack_startup = s_threadinfo[created].stack_max;
219 s_threadinfo[created].stack_min = s_threadinfo[created].stack_max;
bart3772a982008-03-15 08:11:03 +0000220 tl_assert(s_threadinfo[created].stack_max != 0);
sewardjaf44c822007-11-25 14:01:38 +0000221
bart3772a982008-03-15 08:11:03 +0000222 return created;
sewardjaf44c822007-11-25 14:01:38 +0000223}
224
225/* NPTL hack: NPTL allocates the 'struct pthread' on top of the stack, */
226/* and accesses this data structure from multiple threads without locking. */
227/* Any conflicting accesses in the range stack_startup..stack_max will be */
228/* ignored. */
229void thread_set_stack_startup(const DrdThreadId tid, const Addr stack_startup)
230{
231#if 0
bart3772a982008-03-15 08:11:03 +0000232 VG_(message)(Vg_DebugMsg, "thread_set_stack_startup: thread %d (%d)"
233 " stack 0x%x .. 0x%lx (size %d)",
234 s_threadinfo[tid].vg_threadid, tid,
235 stack_startup,
236 s_threadinfo[tid].stack_max,
237 s_threadinfo[tid].stack_max - stack_startup);
sewardjaf44c822007-11-25 14:01:38 +0000238#endif
bart3772a982008-03-15 08:11:03 +0000239 tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID);
240 tl_assert(s_threadinfo[tid].stack_min <= stack_startup);
241 tl_assert(stack_startup <= s_threadinfo[tid].stack_max);
242 s_threadinfo[tid].stack_startup = stack_startup;
sewardjaf44c822007-11-25 14:01:38 +0000243}
244
245Addr thread_get_stack_min(const DrdThreadId tid)
246{
bart3772a982008-03-15 08:11:03 +0000247 tl_assert(0 <= tid && tid < DRD_N_THREADS
248 && tid != DRD_INVALID_THREADID);
249 return s_threadinfo[tid].stack_min;
sewardjaf44c822007-11-25 14:01:38 +0000250}
251
sewardjaf44c822007-11-25 14:01:38 +0000252DrdThreadId thread_lookup_stackaddr(const Addr a,
253 Addr* const stack_min,
254 Addr* const stack_max)
255{
bart3772a982008-03-15 08:11:03 +0000256 unsigned i;
257 for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++)
258 {
259 if (s_threadinfo[i].stack_min <= a && a <= s_threadinfo[i].stack_max)
260 {
261 *stack_min = s_threadinfo[i].stack_min;
262 *stack_max = s_threadinfo[i].stack_max;
263 return i;
264 }
265 }
266 return DRD_INVALID_THREADID;
sewardjaf44c822007-11-25 14:01:38 +0000267}
268
269/**
270 * Clean up thread-specific data structures. Call this just after
271 * pthread_join().
272 */
273void thread_delete(const DrdThreadId tid)
274{
bart3772a982008-03-15 08:11:03 +0000275 Segment* sg;
276 Segment* sg_prev;
sewardjaf44c822007-11-25 14:01:38 +0000277
bart3772a982008-03-15 08:11:03 +0000278 tl_assert(0 <= tid && tid < DRD_N_THREADS
279 && tid != DRD_INVALID_THREADID);
280 tl_assert(s_threadinfo[tid].synchr_nesting == 0);
281 for (sg = s_threadinfo[tid].last; sg; sg = sg_prev)
282 {
283 sg_prev = sg->prev;
284 sg_delete(sg);
285 }
286 s_threadinfo[tid].vg_thread_exists = False;
287 s_threadinfo[tid].posix_thread_exists = False;
288 tl_assert(s_threadinfo[tid].detached_posix_thread == False);
289 s_threadinfo[tid].first = 0;
290 s_threadinfo[tid].last = 0;
sewardjaf44c822007-11-25 14:01:38 +0000291}
292
293/* Called after a thread performed its last memory access and before */
294/* thread_delete() is called. Note: thread_delete() is only called for */
295/* joinable threads, not for detached threads. */
296void thread_finished(const DrdThreadId tid)
297{
bart3772a982008-03-15 08:11:03 +0000298 tl_assert(0 <= tid && tid < DRD_N_THREADS
299 && tid != DRD_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000300
bart3772a982008-03-15 08:11:03 +0000301 thread_stop_using_mem(s_threadinfo[tid].stack_min,
302 s_threadinfo[tid].stack_max);
sewardjaf44c822007-11-25 14:01:38 +0000303
bart3772a982008-03-15 08:11:03 +0000304 s_threadinfo[tid].vg_thread_exists = False;
sewardjaf44c822007-11-25 14:01:38 +0000305
bart3772a982008-03-15 08:11:03 +0000306 if (s_threadinfo[tid].detached_posix_thread)
307 {
308 /* Once a detached thread has finished, its stack is deallocated and */
309 /* should no longer be taken into account when computing the danger set*/
310 s_threadinfo[tid].stack_min = s_threadinfo[tid].stack_max;
sewardjaf44c822007-11-25 14:01:38 +0000311
bart3772a982008-03-15 08:11:03 +0000312 /* For a detached thread, calling pthread_exit() invalidates the */
313 /* POSIX thread ID associated with the detached thread. For joinable */
314 /* POSIX threads however, the POSIX thread ID remains live after the */
315 /* pthread_exit() call until pthread_join() is called. */
316 s_threadinfo[tid].posix_thread_exists = False;
317 }
sewardjaf44c822007-11-25 14:01:38 +0000318}
319
320void thread_set_pthreadid(const DrdThreadId tid, const PThreadId ptid)
321{
bart3772a982008-03-15 08:11:03 +0000322 tl_assert(0 <= tid && tid < DRD_N_THREADS
323 && tid != DRD_INVALID_THREADID);
324 tl_assert(s_threadinfo[tid].pt_threadid == INVALID_POSIX_THREADID);
325 tl_assert(ptid != INVALID_POSIX_THREADID);
326 s_threadinfo[tid].posix_thread_exists = True;
327 s_threadinfo[tid].pt_threadid = ptid;
sewardjaf44c822007-11-25 14:01:38 +0000328}
329
330Bool thread_get_joinable(const DrdThreadId tid)
331{
bart3772a982008-03-15 08:11:03 +0000332 tl_assert(0 <= tid && tid < DRD_N_THREADS
333 && tid != DRD_INVALID_THREADID);
334 return ! s_threadinfo[tid].detached_posix_thread;
sewardjaf44c822007-11-25 14:01:38 +0000335}
336
337void thread_set_joinable(const DrdThreadId tid, const Bool joinable)
338{
bart3772a982008-03-15 08:11:03 +0000339 tl_assert(0 <= tid && tid < DRD_N_THREADS
340 && tid != DRD_INVALID_THREADID);
341 tl_assert(!! joinable == joinable);
342 tl_assert(s_threadinfo[tid].pt_threadid != INVALID_POSIX_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000343#if 0
bart3772a982008-03-15 08:11:03 +0000344 VG_(message)(Vg_DebugMsg,
345 "thread_set_joinable(%d/%d, %s)",
346 tid,
347 s_threadinfo[tid].vg_threadid,
348 joinable ? "joinable" : "detached");
sewardjaf44c822007-11-25 14:01:38 +0000349#endif
bart3772a982008-03-15 08:11:03 +0000350 s_threadinfo[tid].detached_posix_thread = ! joinable;
sewardjaf44c822007-11-25 14:01:38 +0000351}
352
353const char* thread_get_name(const DrdThreadId tid)
354{
bart3772a982008-03-15 08:11:03 +0000355 tl_assert(0 <= tid && tid < DRD_N_THREADS
356 && tid != DRD_INVALID_THREADID);
357 return s_threadinfo[tid].name;
sewardjaf44c822007-11-25 14:01:38 +0000358}
359
360void thread_set_name(const DrdThreadId tid, const char* const name)
361{
bart3772a982008-03-15 08:11:03 +0000362 tl_assert(0 <= tid && tid < DRD_N_THREADS
363 && tid != DRD_INVALID_THREADID);
364 VG_(strncpy)(s_threadinfo[tid].name, name,
365 sizeof(s_threadinfo[tid].name));
366 s_threadinfo[tid].name[sizeof(s_threadinfo[tid].name) - 1] = 0;
sewardjaf44c822007-11-25 14:01:38 +0000367}
368
369void thread_set_name_fmt(const DrdThreadId tid, const char* const fmt,
370 const UWord arg)
371{
bart3772a982008-03-15 08:11:03 +0000372 tl_assert(0 <= tid && tid < DRD_N_THREADS
373 && tid != DRD_INVALID_THREADID);
374 VG_(snprintf)(s_threadinfo[tid].name, sizeof(s_threadinfo[tid].name),
375 fmt, arg);
376 s_threadinfo[tid].name[sizeof(s_threadinfo[tid].name) - 1] = 0;
sewardjaf44c822007-11-25 14:01:38 +0000377}
sewardj8b09d4f2007-12-04 21:27:18 +0000378
sewardj8b09d4f2007-12-04 21:27:18 +0000379void thread_set_vg_running_tid(const ThreadId vg_tid)
sewardjaf44c822007-11-25 14:01:38 +0000380{
bart3772a982008-03-15 08:11:03 +0000381 tl_assert(vg_tid != VG_INVALID_THREADID);
sewardj8b09d4f2007-12-04 21:27:18 +0000382
bart3772a982008-03-15 08:11:03 +0000383 if (vg_tid != s_vg_running_tid)
384 {
385 thread_set_running_tid(vg_tid, VgThreadIdToDrdThreadId(vg_tid));
386 }
sewardj8b09d4f2007-12-04 21:27:18 +0000387
bart3772a982008-03-15 08:11:03 +0000388 tl_assert(s_vg_running_tid != VG_INVALID_THREADID);
389 tl_assert(s_drd_running_tid != DRD_INVALID_THREADID);
sewardj8b09d4f2007-12-04 21:27:18 +0000390}
391
392void thread_set_running_tid(const ThreadId vg_tid, const DrdThreadId drd_tid)
393{
bart3772a982008-03-15 08:11:03 +0000394 tl_assert(vg_tid != VG_INVALID_THREADID);
395 tl_assert(drd_tid != DRD_INVALID_THREADID);
sewardj8b09d4f2007-12-04 21:27:18 +0000396
bart3772a982008-03-15 08:11:03 +0000397 if (vg_tid != s_vg_running_tid)
398 {
399 if (s_trace_context_switches
400 && s_drd_running_tid != DRD_INVALID_THREADID)
401 {
402 VG_(message)(Vg_DebugMsg,
403 "Context switch from thread %d to thread %d",
404 s_drd_running_tid, drd_tid);
405 }
406 s_vg_running_tid = vg_tid;
407 s_drd_running_tid = drd_tid;
408 thread_update_danger_set(drd_tid);
409 s_context_switch_count++;
410 }
sewardj8b09d4f2007-12-04 21:27:18 +0000411
bart3772a982008-03-15 08:11:03 +0000412 tl_assert(s_vg_running_tid != VG_INVALID_THREADID);
413 tl_assert(s_drd_running_tid != DRD_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000414}
415
bart0268dfa2008-03-11 20:10:21 +0000416int thread_enter_synchr(const DrdThreadId tid)
417{
bart3772a982008-03-15 08:11:03 +0000418 tl_assert(IsValidDrdThreadId(tid));
419 return s_threadinfo[tid].synchr_nesting++;
bart0268dfa2008-03-11 20:10:21 +0000420}
421
422int thread_leave_synchr(const DrdThreadId tid)
423{
bart3772a982008-03-15 08:11:03 +0000424 tl_assert(IsValidDrdThreadId(tid));
425 tl_assert(s_threadinfo[tid].synchr_nesting >= 1);
426 return --s_threadinfo[tid].synchr_nesting;
bart0268dfa2008-03-11 20:10:21 +0000427}
428
429int thread_get_synchr_nesting_count(const DrdThreadId tid)
430{
bart3772a982008-03-15 08:11:03 +0000431 tl_assert(IsValidDrdThreadId(tid));
432 return s_threadinfo[tid].synchr_nesting;
bart0268dfa2008-03-11 20:10:21 +0000433}
434
bart1a473c72008-03-13 19:03:38 +0000435/** Append a new segment at the end of the segment list. */
bart26f73e12008-02-24 18:37:08 +0000436static void thread_append_segment(const DrdThreadId tid, Segment* const sg)
sewardjaf44c822007-11-25 14:01:38 +0000437{
bart3772a982008-03-15 08:11:03 +0000438 tl_assert(0 <= tid && tid < DRD_N_THREADS
439 && tid != DRD_INVALID_THREADID);
440 tl_assert(sane_ThreadInfo(&s_threadinfo[tid]));
441 sg->prev = s_threadinfo[tid].last;
442 sg->next = 0;
443 if (s_threadinfo[tid].last)
444 s_threadinfo[tid].last->next = sg;
445 s_threadinfo[tid].last = sg;
446 if (s_threadinfo[tid].first == 0)
447 s_threadinfo[tid].first = sg;
448 tl_assert(sane_ThreadInfo(&s_threadinfo[tid]));
sewardjaf44c822007-11-25 14:01:38 +0000449}
450
bart26f73e12008-02-24 18:37:08 +0000451/** Remove a segment from the segment list of thread threadid, and free the
452 * associated memory.
sewardjaf44c822007-11-25 14:01:38 +0000453 */
bart26f73e12008-02-24 18:37:08 +0000454static void thread_discard_segment(const DrdThreadId tid, Segment* const sg)
sewardjaf44c822007-11-25 14:01:38 +0000455{
bart3772a982008-03-15 08:11:03 +0000456 tl_assert(0 <= tid && tid < DRD_N_THREADS
457 && tid != DRD_INVALID_THREADID);
458 tl_assert(sane_ThreadInfo(&s_threadinfo[tid]));
bart26f73e12008-02-24 18:37:08 +0000459
bart3772a982008-03-15 08:11:03 +0000460 if (sg->prev)
461 sg->prev->next = sg->next;
462 if (sg->next)
463 sg->next->prev = sg->prev;
464 if (sg == s_threadinfo[tid].first)
465 s_threadinfo[tid].first = sg->next;
466 if (sg == s_threadinfo[tid].last)
467 s_threadinfo[tid].last = sg->prev;
468 sg_delete(sg);
469 tl_assert(sane_ThreadInfo(&s_threadinfo[tid]));
sewardjaf44c822007-11-25 14:01:38 +0000470}
471
472VectorClock* thread_get_vc(const DrdThreadId tid)
473{
bart3772a982008-03-15 08:11:03 +0000474 tl_assert(0 <= tid && tid < DRD_N_THREADS
475 && tid != DRD_INVALID_THREADID);
476 tl_assert(s_threadinfo[tid].last);
477 return &s_threadinfo[tid].last->vc;
sewardjaf44c822007-11-25 14:01:38 +0000478}
479
480/**
481 * Compute the minimum of all latest vector clocks of all threads
482 * (Michiel Ronsse calls this "clock snooping" in his papers about DIOTA).
483 * @param vc pointer to a vectorclock, holds result upon return.
484 */
485static void thread_compute_minimum_vc(VectorClock* vc)
486{
bart3772a982008-03-15 08:11:03 +0000487 unsigned i;
488 Bool first;
489 Segment* latest_sg;
sewardjaf44c822007-11-25 14:01:38 +0000490
bart3772a982008-03-15 08:11:03 +0000491 first = True;
492 for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++)
493 {
494 latest_sg = s_threadinfo[i].last;
495 if (latest_sg)
496 {
497 if (first)
498 vc_assign(vc, &latest_sg->vc);
499 else
500 vc_min(vc, &latest_sg->vc);
501 first = False;
502 }
503 }
sewardjaf44c822007-11-25 14:01:38 +0000504}
505
506static void thread_compute_maximum_vc(VectorClock* vc)
507{
bart3772a982008-03-15 08:11:03 +0000508 unsigned i;
509 Bool first;
510 Segment* latest_sg;
sewardjaf44c822007-11-25 14:01:38 +0000511
bart3772a982008-03-15 08:11:03 +0000512 first = True;
513 for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++)
514 {
515 latest_sg = s_threadinfo[i].last;
516 if (latest_sg)
517 {
518 if (first)
519 vc_assign(vc, &latest_sg->vc);
520 else
521 vc_combine(vc, &latest_sg->vc);
522 first = False;
523 }
524 }
sewardjaf44c822007-11-25 14:01:38 +0000525}
526
527/**
bart5bd9f2d2008-03-03 20:31:58 +0000528 * Discard all segments that have a defined order against the latest vector
sewardjaf44c822007-11-25 14:01:38 +0000529 * clock of every thread -- these segments can no longer be involved in a
530 * data race.
531 */
532static void thread_discard_ordered_segments(void)
533{
bart3772a982008-03-15 08:11:03 +0000534 unsigned i;
535 VectorClock thread_vc_min;
sewardjaf44c822007-11-25 14:01:38 +0000536
bart3772a982008-03-15 08:11:03 +0000537 s_discard_ordered_segments_count++;
sewardjaf44c822007-11-25 14:01:38 +0000538
bart3772a982008-03-15 08:11:03 +0000539 vc_init(&thread_vc_min, 0, 0);
540 thread_compute_minimum_vc(&thread_vc_min);
541 if (sg_get_trace())
542 {
543 char msg[256];
544 VectorClock thread_vc_max;
sewardjaf44c822007-11-25 14:01:38 +0000545
bart3772a982008-03-15 08:11:03 +0000546 vc_init(&thread_vc_max, 0, 0);
547 thread_compute_maximum_vc(&thread_vc_max);
548 VG_(snprintf)(msg, sizeof(msg),
549 "Discarding ordered segments -- min vc is ");
550 vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg),
551 &thread_vc_min);
552 VG_(snprintf)(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg),
553 ", max vc is ");
554 vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg),
555 &thread_vc_max);
556 VG_(message)(Vg_DebugMsg, "%s", msg);
557 vc_cleanup(&thread_vc_max);
558 }
sewardjaf44c822007-11-25 14:01:38 +0000559
bart3772a982008-03-15 08:11:03 +0000560 for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++)
561 {
562 Segment* sg;
563 Segment* sg_next;
564 for (sg = s_threadinfo[i].first;
565 sg && (sg_next = sg->next) && vc_lte(&sg->vc, &thread_vc_min);
566 sg = sg_next)
567 {
568 thread_discard_segment(i, sg);
569 }
570 }
571 vc_cleanup(&thread_vc_min);
sewardjaf44c822007-11-25 14:01:38 +0000572}
573
574/**
575 * Create a new segment for the specified thread, and report all data races
576 * of the most recent thread segment with other threads.
577 */
578void thread_new_segment(const DrdThreadId tid)
579{
bart3772a982008-03-15 08:11:03 +0000580 Segment* sg;
sewardjaf44c822007-11-25 14:01:38 +0000581
bart3772a982008-03-15 08:11:03 +0000582 tl_assert(0 <= tid && tid < DRD_N_THREADS
583 && tid != DRD_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000584
bart3772a982008-03-15 08:11:03 +0000585 sg = sg_new(tid, tid);
586 thread_append_segment(tid, sg);
sewardjaf44c822007-11-25 14:01:38 +0000587
bart3772a982008-03-15 08:11:03 +0000588 thread_discard_ordered_segments();
bart26f73e12008-02-24 18:37:08 +0000589
bart3772a982008-03-15 08:11:03 +0000590 if (tid == s_drd_running_tid)
591 {
592 /* Every change in the vector clock of the current thread may cause */
593 /* segments that were previously ordered to this thread to become */
594 /* unordered. Hence, recalculate the danger set if the vector clock */
595 /* of the current thread is updated. */
596 thread_update_danger_set(tid);
597 }
sewardjaf44c822007-11-25 14:01:38 +0000598}
599
bart26f73e12008-02-24 18:37:08 +0000600/** Call this function after thread 'joiner' joined thread 'joinee'. */
sewardjaf44c822007-11-25 14:01:38 +0000601void thread_combine_vc(DrdThreadId joiner, DrdThreadId joinee)
602{
bart3772a982008-03-15 08:11:03 +0000603 tl_assert(joiner != joinee);
604 tl_assert(0 <= joiner && joiner < DRD_N_THREADS
605 && joiner != DRD_INVALID_THREADID);
606 tl_assert(0 <= joinee && joinee < DRD_N_THREADS
607 && joinee != DRD_INVALID_THREADID);
608 tl_assert(s_threadinfo[joiner].last);
609 tl_assert(s_threadinfo[joinee].last);
610 vc_combine(&s_threadinfo[joiner].last->vc, &s_threadinfo[joinee].last->vc);
611 thread_discard_ordered_segments();
sewardjaf44c822007-11-25 14:01:38 +0000612
bart3772a982008-03-15 08:11:03 +0000613 if (joiner == s_drd_running_tid)
614 {
615 thread_update_danger_set(joiner);
616 }
sewardjaf44c822007-11-25 14:01:38 +0000617}
618
bart26f73e12008-02-24 18:37:08 +0000619/** Call this function after thread 'tid' had to wait because of thread
620 * synchronization until the memory accesses in the segment with vector clock
621 * 'vc' finished.
622 */
sewardjaf44c822007-11-25 14:01:38 +0000623void thread_combine_vc2(DrdThreadId tid, const VectorClock* const vc)
624{
bart3772a982008-03-15 08:11:03 +0000625 tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID);
626 tl_assert(s_threadinfo[tid].last);
627 tl_assert(vc);
628 vc_combine(&s_threadinfo[tid].last->vc, vc);
629 thread_discard_ordered_segments();
sewardjaf44c822007-11-25 14:01:38 +0000630}
631
bart26f73e12008-02-24 18:37:08 +0000632/** Call this function whenever a thread is no longer using the memory
633 * [ a1, a2 [, e.g. because of a call to free() or a stack pointer
634 * increase.
635 */
sewardjaf44c822007-11-25 14:01:38 +0000636void thread_stop_using_mem(const Addr a1, const Addr a2)
637{
bart3772a982008-03-15 08:11:03 +0000638 DrdThreadId other_user = DRD_INVALID_THREADID;
sewardjaf44c822007-11-25 14:01:38 +0000639
bart3772a982008-03-15 08:11:03 +0000640 /* For all threads, mark the range [ a1, a2 [ as no longer in use. */
sewardjaf44c822007-11-25 14:01:38 +0000641
bart3772a982008-03-15 08:11:03 +0000642 unsigned i;
643 for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++)
644 {
645 Segment* p;
646 for (p = s_threadinfo[i].first; p; p = p->next)
647 {
648 if (other_user == DRD_INVALID_THREADID
649 && i != s_drd_running_tid
650 && bm_has_any_access(p->bm, a1, a2))
sewardjaf44c822007-11-25 14:01:38 +0000651 {
bart3772a982008-03-15 08:11:03 +0000652 other_user = i;
sewardjaf44c822007-11-25 14:01:38 +0000653 }
bart3772a982008-03-15 08:11:03 +0000654 bm_clear(p->bm, a1, a2);
655 }
656 }
sewardjaf44c822007-11-25 14:01:38 +0000657
bart3772a982008-03-15 08:11:03 +0000658 /* If any other thread had accessed memory in [ a1, a2 [, update the */
659 /* danger set. */
660 if (other_user != DRD_INVALID_THREADID
661 && bm_has_any_access(s_danger_set, a1, a2))
662 {
663 thread_update_danger_set(thread_get_running_tid());
664 }
sewardjaf44c822007-11-25 14:01:38 +0000665}
666
bart0268dfa2008-03-11 20:10:21 +0000667void thread_start_recording(const DrdThreadId tid)
668{
bart3772a982008-03-15 08:11:03 +0000669 tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID);
670 tl_assert(! s_threadinfo[tid].is_recording);
671 s_threadinfo[tid].is_recording = True;
bart0268dfa2008-03-11 20:10:21 +0000672}
673
674void thread_stop_recording(const DrdThreadId tid)
675{
bart3772a982008-03-15 08:11:03 +0000676 tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID);
677 tl_assert(s_threadinfo[tid].is_recording);
678 s_threadinfo[tid].is_recording = False;
bart0268dfa2008-03-11 20:10:21 +0000679}
680
sewardjaf44c822007-11-25 14:01:38 +0000681void thread_print_all(void)
682{
bart3772a982008-03-15 08:11:03 +0000683 unsigned i;
684 Segment* p;
sewardjaf44c822007-11-25 14:01:38 +0000685
bart3772a982008-03-15 08:11:03 +0000686 for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++)
687 {
688 if (s_threadinfo[i].first)
689 {
690 VG_(printf)("**************\n"
691 "* thread %3d (%d/%d/%d/0x%x/%d/%s) *\n"
692 "**************\n",
693 i,
694 s_threadinfo[i].vg_thread_exists,
695 s_threadinfo[i].vg_threadid,
696 s_threadinfo[i].posix_thread_exists,
697 s_threadinfo[i].pt_threadid,
698 s_threadinfo[i].detached_posix_thread,
699 s_threadinfo[i].name);
700 for (p = s_threadinfo[i].first; p; p = p->next)
sewardjaf44c822007-11-25 14:01:38 +0000701 {
bart3772a982008-03-15 08:11:03 +0000702 sg_print(p);
sewardjaf44c822007-11-25 14:01:38 +0000703 }
bart3772a982008-03-15 08:11:03 +0000704 }
705 }
sewardjaf44c822007-11-25 14:01:38 +0000706}
707
708static void show_call_stack(const DrdThreadId tid,
709 const Char* const msg,
710 ExeContext* const callstack)
711{
bart3772a982008-03-15 08:11:03 +0000712 const ThreadId vg_tid = DrdThreadIdToVgThreadId(tid);
sewardjaf44c822007-11-25 14:01:38 +0000713
bart3772a982008-03-15 08:11:03 +0000714 VG_(message)(Vg_UserMsg,
715 "%s (%s)",
716 msg,
717 thread_get_name(tid));
sewardjaf44c822007-11-25 14:01:38 +0000718
bart3772a982008-03-15 08:11:03 +0000719 if (vg_tid != VG_INVALID_THREADID)
720 {
721 if (callstack)
722 {
723 VG_(pp_ExeContext)(callstack);
724 }
725 else
726 {
727 VG_(get_and_pp_StackTrace)(vg_tid, VG_(clo_backtrace_size));
728 }
729 }
730 else
731 {
732 VG_(message)(Vg_UserMsg,
733 " (thread finished, call stack no longer available)");
734 }
sewardjaf44c822007-11-25 14:01:38 +0000735}
736
sewardjaf44c822007-11-25 14:01:38 +0000737static void
738thread_report_conflicting_segments_segment(const DrdThreadId tid,
739 const Addr addr,
740 const SizeT size,
741 const BmAccessTypeT access_type,
742 const Segment* const p)
743{
bart3772a982008-03-15 08:11:03 +0000744 unsigned i;
sewardjaf44c822007-11-25 14:01:38 +0000745
bart3772a982008-03-15 08:11:03 +0000746 tl_assert(0 <= tid && tid < DRD_N_THREADS
747 && tid != DRD_INVALID_THREADID);
748 tl_assert(p);
sewardjaf44c822007-11-25 14:01:38 +0000749
bart3772a982008-03-15 08:11:03 +0000750 for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++)
751 {
752 if (i != tid)
753 {
754 Segment* q;
755 for (q = s_threadinfo[i].last; q; q = q->prev)
sewardjaf44c822007-11-25 14:01:38 +0000756 {
bart3772a982008-03-15 08:11:03 +0000757 // Since q iterates over the segments of thread i in order of
758 // decreasing vector clocks, if q->vc <= p->vc, then
759 // q->next->vc <= p->vc will also hold. Hence, break out of the
760 // loop once this condition is met.
761 if (vc_lte(&q->vc, &p->vc))
762 break;
763 if (! vc_lte(&p->vc, &q->vc))
764 {
765 if (bm_has_conflict_with(q->bm, addr, addr + size, access_type))
766 {
767 tl_assert(q->stacktrace);
768 show_call_stack(i, "Other segment start",
769 q->stacktrace);
770 show_call_stack(i, "Other segment end",
771 q->next ? q->next->stacktrace : 0);
772 }
773 }
sewardjaf44c822007-11-25 14:01:38 +0000774 }
bart3772a982008-03-15 08:11:03 +0000775 }
776 }
sewardjaf44c822007-11-25 14:01:38 +0000777}
778
779void thread_report_conflicting_segments(const DrdThreadId tid,
780 const Addr addr,
781 const SizeT size,
782 const BmAccessTypeT access_type)
783{
bart3772a982008-03-15 08:11:03 +0000784 Segment* p;
sewardjaf44c822007-11-25 14:01:38 +0000785
bart3772a982008-03-15 08:11:03 +0000786 tl_assert(0 <= tid && tid < DRD_N_THREADS
787 && tid != DRD_INVALID_THREADID);
sewardjaf44c822007-11-25 14:01:38 +0000788
bart3772a982008-03-15 08:11:03 +0000789 for (p = s_threadinfo[tid].first; p; p = p->next)
790 {
791 if (bm_has(p->bm, addr, addr + size, access_type))
792 {
793 thread_report_conflicting_segments_segment(tid, addr, size,
794 access_type, p);
795 }
796 }
sewardjaf44c822007-11-25 14:01:38 +0000797}
sewardjaf44c822007-11-25 14:01:38 +0000798
bart26f73e12008-02-24 18:37:08 +0000799/** Compute a bitmap that represents the union of all memory accesses of all
800 * segments that are unordered to the current segment of the thread tid.
sewardjaf44c822007-11-25 14:01:38 +0000801 */
802static void thread_update_danger_set(const DrdThreadId tid)
803{
bart3772a982008-03-15 08:11:03 +0000804 Segment* p;
sewardjaf44c822007-11-25 14:01:38 +0000805
bart3772a982008-03-15 08:11:03 +0000806 tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID);
807 tl_assert(tid == s_drd_running_tid);
sewardjaf44c822007-11-25 14:01:38 +0000808
bart3772a982008-03-15 08:11:03 +0000809 s_update_danger_set_count++;
810 s_danger_set_bitmap_creation_count -= bm_get_bitmap_creation_count();
811 s_danger_set_bitmap2_creation_count -= bm_get_bitmap2_creation_count();
sewardjaf44c822007-11-25 14:01:38 +0000812
bart3772a982008-03-15 08:11:03 +0000813 if (s_danger_set)
814 {
bart1ea5fff2008-03-16 08:36:23 +0000815 bm_delete(s_danger_set);
bart3772a982008-03-15 08:11:03 +0000816 }
bart1ea5fff2008-03-16 08:36:23 +0000817 s_danger_set = bm_new();
bart26f73e12008-02-24 18:37:08 +0000818
bart3772a982008-03-15 08:11:03 +0000819 if (s_trace_danger_set)
820 {
821 char msg[256];
822
823 VG_(snprintf)(msg, sizeof(msg),
824 "computing danger set for thread %d with vc ",
825 tid);
826 vc_snprint(msg + VG_(strlen)(msg),
827 sizeof(msg) - VG_(strlen)(msg),
828 &s_threadinfo[tid].last->vc);
829 VG_(message)(Vg_DebugMsg, "%s", msg);
830 }
831
832 p = s_threadinfo[tid].last;
833 {
834 unsigned j;
835
836 if (s_trace_danger_set)
837 {
bart26f73e12008-02-24 18:37:08 +0000838 char msg[256];
839
840 VG_(snprintf)(msg, sizeof(msg),
bart3772a982008-03-15 08:11:03 +0000841 "danger set: thread [%d] at vc ",
bart26f73e12008-02-24 18:37:08 +0000842 tid);
843 vc_snprint(msg + VG_(strlen)(msg),
844 sizeof(msg) - VG_(strlen)(msg),
bart3772a982008-03-15 08:11:03 +0000845 &p->vc);
bart26f73e12008-02-24 18:37:08 +0000846 VG_(message)(Vg_DebugMsg, "%s", msg);
bart3772a982008-03-15 08:11:03 +0000847 }
sewardjaf44c822007-11-25 14:01:38 +0000848
bart3772a982008-03-15 08:11:03 +0000849 for (j = 0; j < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); j++)
850 {
851 if (IsValidDrdThreadId(j))
bart26f73e12008-02-24 18:37:08 +0000852 {
bart3772a982008-03-15 08:11:03 +0000853 const Segment* q;
854 for (q = s_threadinfo[j].last; q; q = q->prev)
855 if (j != tid && q != 0
856 && ! vc_lte(&q->vc, &p->vc) && ! vc_lte(&p->vc, &q->vc))
857 {
858 if (s_trace_danger_set)
859 {
860 char msg[256];
861 VG_(snprintf)(msg, sizeof(msg),
862 "danger set: [%d] merging segment ", j);
863 vc_snprint(msg + VG_(strlen)(msg),
864 sizeof(msg) - VG_(strlen)(msg),
865 &q->vc);
866 VG_(message)(Vg_DebugMsg, "%s", msg);
867 }
868 bm_merge2(s_danger_set, q->bm);
869 }
870 else
871 {
872 if (s_trace_danger_set)
873 {
874 char msg[256];
875 VG_(snprintf)(msg, sizeof(msg),
876 "danger set: [%d] ignoring segment ", j);
877 vc_snprint(msg + VG_(strlen)(msg),
878 sizeof(msg) - VG_(strlen)(msg),
879 &q->vc);
880 VG_(message)(Vg_DebugMsg, "%s", msg);
881 }
882 }
bart26f73e12008-02-24 18:37:08 +0000883 }
bart3772a982008-03-15 08:11:03 +0000884 }
bart26f73e12008-02-24 18:37:08 +0000885
bart3772a982008-03-15 08:11:03 +0000886 for (j = 0; j < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); j++)
887 {
888 if (IsValidDrdThreadId(j))
sewardjaf44c822007-11-25 14:01:38 +0000889 {
bart3772a982008-03-15 08:11:03 +0000890 // NPTL hack: don't report data races on sizeof(struct pthread)
891 // bytes at the top of the stack, since the NPTL functions access
892 // this data without locking.
893 if (s_threadinfo[j].stack_min != 0)
894 {
895 tl_assert(s_threadinfo[j].stack_startup != 0);
896 if (s_threadinfo[j].stack_min < s_threadinfo[j].stack_startup)
897 {
898 bm_clear(s_danger_set,
899 s_threadinfo[j].stack_min,
900 s_threadinfo[j].stack_startup);
901 }
902 }
sewardjaf44c822007-11-25 14:01:38 +0000903 }
bart3772a982008-03-15 08:11:03 +0000904 }
905 }
sewardjaf44c822007-11-25 14:01:38 +0000906
bart3772a982008-03-15 08:11:03 +0000907 s_danger_set_bitmap_creation_count += bm_get_bitmap_creation_count();
908 s_danger_set_bitmap2_creation_count += bm_get_bitmap2_creation_count();
sewardjaf44c822007-11-25 14:01:38 +0000909
bart3772a982008-03-15 08:11:03 +0000910 if (0 && s_trace_danger_set)
911 {
912 VG_(message)(Vg_DebugMsg, "[%d] new danger set:", tid);
913 bm_print(s_danger_set);
914 VG_(message)(Vg_DebugMsg, "[%d] end of new danger set.", tid);
915 }
sewardjaf44c822007-11-25 14:01:38 +0000916}
917
sewardjaf44c822007-11-25 14:01:38 +0000918ULong thread_get_context_switch_count(void)
919{
bart3772a982008-03-15 08:11:03 +0000920 return s_context_switch_count;
sewardjaf44c822007-11-25 14:01:38 +0000921}
922
sewardjaf44c822007-11-25 14:01:38 +0000923ULong thread_get_discard_ordered_segments_count(void)
924{
bart3772a982008-03-15 08:11:03 +0000925 return s_discard_ordered_segments_count;
sewardjaf44c822007-11-25 14:01:38 +0000926}
927
928ULong thread_get_update_danger_set_count(void)
929{
bart3772a982008-03-15 08:11:03 +0000930 return s_update_danger_set_count;
sewardjaf44c822007-11-25 14:01:38 +0000931}
932
933ULong thread_get_danger_set_bitmap_creation_count(void)
934{
bart3772a982008-03-15 08:11:03 +0000935 return s_danger_set_bitmap_creation_count;
sewardjaf44c822007-11-25 14:01:38 +0000936}
937
938ULong thread_get_danger_set_bitmap2_creation_count(void)
939{
bart3772a982008-03-15 08:11:03 +0000940 return s_danger_set_bitmap2_creation_count;
sewardjaf44c822007-11-25 14:01:38 +0000941}