2 This file is part of the NoBug debugging library.
4 Copyright (C) 2007, 2008, 2009, Christian Thaeter <ct@pipapo.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, contact Christian Thaeter <ct@pipapo.org>.
21 #define NOBUG_LIBNOBUG_C
27 //deadlock There are 2 kinds of nodes: `resource_record` hold registered resources and `resource_user` which
28 //deadlock attach to (enter) a resource.
30 //deadlock Each thread keeps stacklist of each `resource_user` it created, new enters will push on the stack,
31 //deadlock leaving a resource will remove it from this stacklist.
33 //deadlock All `resource_records` in use are linked in a global precedence list, items of equal precedence are
34 //deadlock spaned by a skip pointer. Whenever a resource is entered the deadlock checker asserts that one does
35 //deadlock not break existing precedences. By doing so the precedence list gets continously refined as the system
36 //deadlock learns about new lock patterns.
38 //deadlock As a consequence of this algorithm the deadlock checker does not only find real deadlocks but
39 //deadlock already potential deadlocks by violations of the locking order which is a lot simpler than finding
40 //deadlock actual deadlocks.
42 //deadlock This also means that the deadlock tracker currently only works with hierarchical locking policies
43 //deadlock other approaches to prevent deadlocks are not yet supported and will be added on demand.
50 How much memory to reserve for a mpool chunk, 16k by default
52 #ifndef NOBUG_RESOURCE_MPOOL_CHUNKSIZE
53 #define NOBUG_RESOURCE_MPOOL_CHUNKSIZE (4096<<(sizeof(void*)/4)) /* That is roughly 8k chunks on 32bit, 16k chunks on 64 bit machines */
57 pthread_mutex_t nobug_resource_mutex
;
60 #define nobug_resourcestates \
61 resource_state(invalid), \
62 resource_state(waiting), \
63 resource_state(trying), \
64 resource_state(exclusive), \
65 resource_state(recursive), \
66 resource_state(shared),
69 #define resource_state(name) #name
70 const char* nobug_resource_states
[] =
77 const char* nobug_resource_error
= NULL
;
79 static llist nobug_resource_registry
;
80 static mpool nobug_resource_record_pool
;
81 static mpool nobug_resource_user_pool
;
83 static mpool nobug_resource_node_pool
;
86 static void nobug_resource_record_dtor (void*);
87 static void nobug_resource_user_dtor (void*);
89 static void nobug_resource_node_dtor (void*);
94 nobug_resource_init (void)
97 static pthread_mutexattr_t attr
;
98 pthread_mutexattr_init (&attr
);
99 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_RECURSIVE
);
100 pthread_mutex_init (&nobug_resource_mutex
, &attr
);
103 llist_init (&nobug_resource_registry
);
105 mpool_init (&nobug_resource_record_pool
,
106 sizeof(struct nobug_resource_record
),
107 NOBUG_RESOURCE_MPOOL_CHUNKSIZE
/sizeof(struct nobug_resource_record
),
108 nobug_resource_record_dtor
);
110 mpool_init (&nobug_resource_user_pool
,
111 sizeof(struct nobug_resource_user
),
112 NOBUG_RESOURCE_MPOOL_CHUNKSIZE
/sizeof(struct nobug_resource_user
),
113 nobug_resource_user_dtor
);
115 #if NOBUG_USE_PTHREAD
116 mpool_init (&nobug_resource_node_pool
,
117 sizeof(struct nobug_resource_node
),
118 NOBUG_RESOURCE_MPOOL_CHUNKSIZE
/sizeof(struct nobug_resource_node
),
119 nobug_resource_node_dtor
);
125 nobug_resource_destroy (void)
127 #if NOBUG_USE_PTHREAD
128 mpool_destroy (&nobug_resource_node_pool
);
130 mpool_destroy (&nobug_resource_user_pool
);
131 mpool_destroy (&nobug_resource_record_pool
);
136 nobug_resource_record_available (void)
138 return mpool_available (&nobug_resource_record_pool
);
143 nobug_resource_user_available (void)
145 return mpool_available (&nobug_resource_user_pool
);
149 #if NOBUG_USE_PTHREAD
151 nobug_resource_node_available (void)
153 return mpool_available (&nobug_resource_node_pool
);
158 nobug_resource_node_free (struct nobug_resource_node
* self
)
160 LLIST_WHILE_HEAD (&self
->childs
, c
)
161 nobug_resource_node_free (LLIST_TO_STRUCTP(c
, struct nobug_resource_node
, cldnode
));
163 llist_unlink_fast_ (&self
->cldnode
);
164 llist_unlink_fast_ (&self
->node
);
165 mpool_free (&nobug_resource_node_pool
, self
);
170 nobug_resource_node_dtor (void* p
)
172 struct nobug_resource_node
* n
= p
;
173 llist_unlink_fast_ (&n
->node
);
174 /* must unlink childs, because we don't destroy the tree bottom up */
175 llist_unlink_fast_ (&n
->childs
);
176 llist_unlink_fast_ (&n
->cldnode
);
182 nobug_resource_record_dtor (void* p
)
184 struct nobug_resource_record
* self
= p
;
185 llist_unlink_fast_ (&self
->hdr
.node
);
187 #if NOBUG_USE_PTHREAD
188 /* destroy all nodes recursively */
189 LLIST_WHILE_HEAD (&self
->nodes
, n
)
190 nobug_resource_node_free ((struct nobug_resource_node
*)n
);
196 nobug_resource_user_dtor (void* p
)
198 struct nobug_resource_user
* u
= p
;
199 llist_unlink_fast_ (&u
->hdr
.node
);
200 #if NOBUG_USE_PTHREAD
201 llist_unlink_fast_ (&u
->res_stack
);
207 compare_resource_records (const_LList av
, const_LList bv
, void* unused
)
210 const struct nobug_resource_record
* a
= (const struct nobug_resource_record
*)av
;
211 const struct nobug_resource_record
* b
= (const struct nobug_resource_record
*)bv
;
213 return a
->object_id
> b
->object_id
? 1 : a
->object_id
< b
->object_id
? -1 : 0;
217 struct nobug_resource_record
*
218 nobug_resource_announce (const char* type
, const char* name
, const void* object_id
, const char* extra
)
221 struct nobug_resource_record
* node
= mpool_alloc (&nobug_resource_record_pool
);
224 nobug_resource_error
= "internal allocation error";
228 node
->hdr
.name
= name
;
229 node
->object_id
= object_id
;
232 /* TODO better lookup method than list search (psplay?) */
233 if (llist_ufind (&nobug_resource_registry
, &node
->hdr
.node
, compare_resource_records
, NULL
))
235 nobug_resource_error
= "already registered";
239 llist_init (&node
->users
);
240 node
->hdr
.extra
= extra
;
241 #if NOBUG_USE_PTHREAD
242 llist_init (&node
->nodes
);
245 llist_insert_head (&nobug_resource_registry
, llist_init (&node
->hdr
.node
));
252 nobug_resource_forget (struct nobug_resource_record
* self
)
254 if (!llist_find (&nobug_resource_registry
, &self
->hdr
.node
, compare_resource_records
, NULL
))
256 nobug_resource_error
= "not registered";
260 if (!llist_is_empty (&self
->users
))
262 nobug_resource_error
= "still in use";
266 nobug_resource_record_dtor (self
);
268 mpool_free (&nobug_resource_record_pool
, self
);
273 #if NOBUG_USE_PTHREAD
275 nobug_resource_node_resource_cmpfn (const_LList a
, const_LList b
, void* extra
)
278 return ((struct nobug_resource_node
*)a
)->resource
==
279 ((struct nobug_resource_node
*)b
)->resource
?0:-1;
283 struct nobug_resource_node
*
284 nobug_resource_node_new (struct nobug_resource_record
* resource
,
285 struct nobug_resource_node
* parent
)
287 struct nobug_resource_node
* self
= mpool_alloc (&nobug_resource_node_pool
);
290 llist_insert_head (&resource
->nodes
, llist_init (&self
->node
));
291 self
->resource
= resource
;
293 self
->parent
= parent
;
295 llist_init (&self
->childs
);
296 llist_init (&self
->cldnode
);
298 llist_insert_head (&parent
->childs
, &self
->cldnode
);
305 //dlalgo HEAD~ The Resource Tracking Algorithm; deadlock_detection; how resources are tracked
307 //dlalgo Each resource registers a global 'resource_record'.
309 //dlalgo Every new locking path discovered is stored as 'resource_node' structures which refer to the associated
310 //dlalgo 'resource_record'.
312 //dlalgo Threads keep a trail of 'resource_user' strcutures for each resource entered. This 'resource_user' struct
313 //dlalgo refer to the 'resource_nodes' and thus indirectly to the associated 'resource_record'.
315 //dlalgo The deadlock checker uses this information to test if the acqusition of a new resource would yield a
316 //dlalgo potential deadlock.
318 struct nobug_resource_user
*
319 nobug_resource_enter (struct nobug_resource_record
* resource
,
320 const char* identifier
,
321 enum nobug_resource_state state
,
326 nobug_resource_error
= "no resource";
330 #if NOBUG_USE_PTHREAD
331 struct nobug_tls_data
* tls
= nobug_thread_get ();
333 //dlalgo HEAD^ Entering Resources; nobug_resource_enter; deadlock check on enter
335 //dlalgo In multithreaded programs, whenever a thread wants to wait for a 'resource_record'
336 //dlalgo the deadlock checker jumps in.
338 //dlalgo The deadlock checking algorithm is anticipatory as it will find and abort on conditions which may lead
339 //dlalgo to a potential deadlock by violating the locking order learned earlier.
341 //dlalgo Each thread holds a stack (list) of each 'resource_user' it created. Leaving
342 //dlalgo a resource will remove it from this stacklist.
344 //dlalgo Each 'resource_record' stores the trail which other 'resource_records' are already entered. This relations
345 //dlalgo are implemented with the 'resource_node' helper structure.
348 //dlalgo TODO: insert diagram here
361 //dlalgo First we find out if there is already a node from the to be acquired resource back to
362 //dlalgo the topmost node of the current threads user stack.
365 //dlalgo ---------------------------------------------------------------------
366 struct nobug_resource_user
* user
= NULL
; //dlalgo VERBATIM
367 struct nobug_resource_node
* node
= NULL
; //dlalgo VERBATIM
369 if (!llist_is_empty (&tls
->res_stack
)) //dlalgo VERBATIM
371 user
= LLIST_TO_STRUCTP (llist_tail (&tls
->res_stack
), //dlalgo VERBATIM
372 struct nobug_resource_user
, //dlalgo VERBATIM
373 res_stack
); //dlalgo VERBATIM
375 struct nobug_resource_node templ
= //dlalgo VERBATIM
377 {NULL
, NULL
}, //dlalgo ...
378 user
->current
->resource
, //dlalgo VERBATIM
384 node
= (struct nobug_resource_node
*) //dlalgo VERBATIM
385 llist_ufind (&resource
->nodes
, //dlalgo VERBATIM
386 &templ
.node
, //dlalgo VERBATIM
387 nobug_resource_node_resource_cmpfn
, //dlalgo VERBATIM
388 NULL
); //dlalgo VERBATIM
391 //dlalgo ---------------------------------------------------------------------
395 //dlalgo Deadlock checking is only done when the node is entered in `WAITING` state and only
396 //dlalgo available in multithreaded programs.
399 //dlalgo ---------------------------------------------------------------------
400 if (state
== NOBUG_RESOURCE_WAITING
) //dlalgo VERBATIM
402 #if NOBUG_USE_PTHREAD //dlalgo VERBATIM
404 //dlalgo ---------------------------------------------------------------------
407 //dlalgo If node was found above, then this locking path is already validated and no deadlock can happen,
408 //dlalgo else, if this stack already holds a resource (user is set) we have to go on with checking.
411 //dlalgo ---------------------------------------------------------------------
412 if (!node
&& user
) //dlalgo VERBATIM
415 //dlalgo ---------------------------------------------------------------------
417 //dlalgo If not then its checked that the resource to be entered is not on any parent trail of the current topmost resource,
418 //dlalgo if it is then this could be a deadlock which needs to be further investigated.
421 //dlalgo ---------------------------------------------------------------------
422 LLIST_FOREACH (&user
->current
->resource
->nodes
, n
) //dlalgo VERBATIM
424 for (struct nobug_resource_node
* itr
= //dlalgo VERBATIM
425 ((struct nobug_resource_node
*)n
)->parent
; //dlalgo VERBATIM
426 itr
; //dlalgo VERBATIM
427 itr
= itr
->parent
) //dlalgo VERBATIM
429 if (itr
->resource
== resource
) //dlalgo VERBATIM
432 //dlalgo ---------------------------------------------------------------------
434 //dlalgo if the resource was on the trail, we search if there is a common ancestor before the resource
435 //dlalgo on the trail and the threads current chain,
436 //dlalgo if yes then this ancestor protects against deadlocks and we can continue.
439 //dlalgo ---------------------------------------------------------------------
440 for (struct nobug_resource_node
* itr2
= itr
->parent
; //dlalgo VERBATIM
441 itr2
; //dlalgo VERBATIM
442 itr2
= itr2
->parent
) //dlalgo VERBATIM
444 LLIST_FOREACH_REV (&tls
->res_stack
, p
) //dlalgo VERBATIM
446 struct nobug_resource_user
* user
= //dlalgo VERBATIM
447 LLIST_TO_STRUCTP (p
, //dlalgo VERBATIM
448 struct nobug_resource_user
, //dlalgo VERBATIM
449 res_stack
); //dlalgo VERBATIM
450 if (user
->current
->resource
== itr2
->resource
) //dlalgo VERBATIM
451 goto done
; //dlalgo VERBATIM
453 //dlalgo ---------------------------------------------------------------------
455 //dlalgo If no ancestor found, we finally abort with a potential deadlock condition.
458 //dlalgo ---------------------------------------------------------------------
459 nobug_resource_error
= "possible deadlock detected"; //dlalgo VERBATIM
460 return NULL
; //dlalgo VERBATIM
462 //dlalgo ---------------------------------------------------------------------
472 else if (state
== NOBUG_RESOURCE_TRYING
)
476 else if (state
== NOBUG_RESOURCE_EXCLUSIVE
)
478 /* check that everyone is waiting */
479 LLIST_FOREACH (&resource
->users
, n
)
480 if (((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_WAITING
&&
481 ((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_TRYING
)
483 nobug_resource_error
= "invalid enter state (resource already claimed)";
487 #if NOBUG_USE_PTHREAD
488 else if (state
== NOBUG_RESOURCE_RECURSIVE
)
490 /* check that everyone *else* is waiting */
491 LLIST_FOREACH (&resource
->users
, n
)
493 struct nobug_resource_user
* user
= (struct nobug_resource_user
*)n
;
494 if (user
->state
!= NOBUG_RESOURCE_WAITING
&&
495 user
->state
!= NOBUG_RESOURCE_TRYING
&&
498 nobug_resource_error
= "invalid enter state (resource already claimed non recursive by another thread)";
501 else if (!(user
->state
== NOBUG_RESOURCE_WAITING
||
502 user
->state
== NOBUG_RESOURCE_TRYING
||
503 user
->state
== NOBUG_RESOURCE_RECURSIVE
) &&
506 nobug_resource_error
= "invalid enter state (resource already claimed non recursive by this thread)";
512 else if (state
== NOBUG_RESOURCE_SHARED
)
514 /* check that every one else is waiting or hold it shared */
515 LLIST_FOREACH (&resource
->users
, n
)
516 if (((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_WAITING
&&
517 ((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_TRYING
&&
518 ((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_SHARED
)
520 nobug_resource_error
= "invalid enter state (resource already claimed non shared)";
525 nobug_resource_error
= "invalid enter state";
527 if (nobug_resource_error
)
530 struct nobug_resource_user
* new_user
= mpool_alloc (&nobug_resource_user_pool
);
533 nobug_resource_error
= "internal allocation error";
537 new_user
->hdr
.name
= identifier
;
538 new_user
->hdr
.extra
= extra
;
539 new_user
->state
= state
;
540 llist_insert_head (&resource
->users
, llist_init (&new_user
->hdr
.node
));
542 #if NOBUG_USE_PTHREAD
545 /* no node found, create a new one */
546 node
= nobug_resource_node_new (resource
, user
?user
->current
:NULL
);
549 nobug_resource_error
= "internal allocation error";
554 new_user
->current
= node
;
555 new_user
->thread
= tls
;
556 llist_insert_tail (&tls
->res_stack
, llist_init (&new_user
->res_stack
));
563 #if NOBUG_USE_PTHREAD
565 nobug_resource_node_parent_cmpfn (const_LList a
, const_LList b
, void* extra
)
568 return ((struct nobug_resource_node
*)a
)->parent
==
569 ((struct nobug_resource_node
*)b
)->parent
?0:-1;
575 nobug_resource_leave (struct nobug_resource_user
* user
)
579 nobug_resource_error
= "no handle";
584 if (!user
->current
?user
->current
->resource
:NULL
)
586 nobug_resource_error
= "not entered";
592 //dlalgo HEAD^ Leaving Resources; nobug_resource_leave; fix resource lists
594 //dlalgo store the tail and next aside, we need it later
597 //dlalgo ---------------------------------------------------------------------
598 #if NOBUG_USE_PTHREAD //dlalgo VERBATIM
599 struct nobug_resource_user
* tail
= //dlalgo VERBATIM
600 LLIST_TO_STRUCTP (llist_tail (&user
->thread
->res_stack
), //dlalgo VERBATIM
601 struct nobug_resource_user
, //dlalgo VERBATIM
602 res_stack
); //dlalgo VERBATIM
604 struct nobug_resource_user
* next
= //dlalgo VERBATIM
605 LLIST_TO_STRUCTP (llist_next (&user
->res_stack
), //dlalgo VERBATIM
606 struct nobug_resource_user
, //dlalgo VERBATIM
607 res_stack
); //dlalgo VERBATIM
608 //dlalgo ---------------------------------------------------------------------
610 //dlalgo remove user struct from thread stack
611 //dlalgo The res_stack is now like it is supposed to look like with the 'user' removed.
612 //dlalgo We now need to fix the node tree up to match this list.
615 //dlalgo ---------------------------------------------------------------------
616 llist_unlink_fast_ (&user
->res_stack
); //dlalgo VERBATIM
617 //dlalgo ---------------------------------------------------------------------
619 //dlalgo When the the user node was not the tail or only node of the thread stack, we have to check
620 //dlalgo (and possibly construct) a new node chain for it. No valdation of this chain needs to be done,
621 //dlalgo since it was already validated when entering the resources first.
624 //dlalgo ---------------------------------------------------------------------
625 if (user
!= tail
&& !llist_is_empty (&user
->thread
->res_stack
)) //dlalgo VERBATIM
627 struct nobug_resource_user
* parent
= NULL
; //dlalgo VERBATIM
628 if (llist_head (&user
->thread
->res_stack
) != &next
->res_stack
) //dlalgo VERBATIM
630 parent
= //dlalgo VERBATIM
631 LLIST_TO_STRUCTP (llist_prev (&next
->res_stack
), //dlalgo VERBATIM
632 struct nobug_resource_user
, //dlalgo VERBATIM
633 res_stack
); //dlalgo VERBATIM
635 //dlalgo ---------------------------------------------------------------------
637 //dlalgo iterate over all users following the removed node, finding nodes pointing to this users or
638 //dlalgo create new nodes.
641 //dlalgo ---------------------------------------------------------------------
642 LLIST_FORRANGE (&next
->res_stack
, &user
->thread
->res_stack
, n
) //dlalgo VERBATIM
644 struct nobug_resource_user
* cur
= //dlalgo VERBATIM
645 LLIST_TO_STRUCTP (n
, //dlalgo VERBATIM
646 struct nobug_resource_user
, //dlalgo VERBATIM
647 res_stack
); //dlalgo VERBATIM
649 struct nobug_resource_record
* resource
= cur
->current
->resource
;
651 //dlalgo ---------------------------------------------------------------------
652 //TODO this search could be optimized out after we creates a node once,
653 //TODO all following nodes need to be created too
655 //dlalgo find the node pointing back to parent, create a new one if not found, rinse repeat
658 //dlalgo ---------------------------------------------------------------------
659 struct nobug_resource_node templ
= //dlalgo VERBATIM
661 {NULL
, NULL
}, //dlalgo ...
662 NULL
, //dlalgo VERBATIM
663 parent
?parent
->current
:NULL
, //dlalgo ...
668 struct nobug_resource_node
* node
= (struct nobug_resource_node
*) //dlalgo VERBATIM
669 llist_ufind (&resource
->nodes
, //dlalgo VERBATIM
670 &templ
.node
, //dlalgo VERBATIM
671 nobug_resource_node_parent_cmpfn
, //dlalgo VERBATIM
672 NULL
); //dlalgo VERBATIM
674 if (!node
) //dlalgo VERBATIM
676 node
= nobug_resource_node_new (resource
, //dlalgo VERBATIM
677 parent
?parent
->current
:NULL
); //dlalgo VERBATIM
678 if (!node
) //dlalgo VERBATIM
680 nobug_resource_error
= "internal allocation error"; //dlalgo VERBATIM
681 return 0; //dlalgo VERBATIM
685 parent
= cur
; //dlalgo VERBATIM
688 //dlalgo ---------------------------------------------------------------------
692 llist_unlink_fast_ (&user
->hdr
.node
);
693 mpool_free (&nobug_resource_user_pool
, user
);
701 nobug_resource_state (struct nobug_resource_user
* user
,
702 enum nobug_resource_state state
)
706 nobug_resource_error
= "no user handle";
710 if (user
->state
== NOBUG_RESOURCE_WAITING
|| user
->state
== NOBUG_RESOURCE_TRYING
)
712 if (state
== NOBUG_RESOURCE_EXCLUSIVE
)
714 /* check that every one is waiting */
715 LLIST_FOREACH (&user
->current
->resource
->users
, n
)
716 if (((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_WAITING
)
718 nobug_resource_error
= "resource hold by another thread";
722 #if NOBUG_USE_PTHREAD
723 else if (state
== NOBUG_RESOURCE_RECURSIVE
)
725 /* check that every one else is waiting */
726 LLIST_FOREACH (&user
->current
->resource
->users
, n
)
727 if (((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_WAITING
&&
728 ((struct nobug_resource_user
*)n
)->thread
!= nobug_thread_get ())
730 nobug_resource_error
= "resource hold by another thread";
735 else if (state
== NOBUG_RESOURCE_SHARED
)
737 /* check that every one else is waiting or shared */
738 LLIST_FOREACH (&user
->current
->resource
->users
, n
)
739 if (((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_WAITING
740 && ((struct nobug_resource_user
*)n
)->state
!= NOBUG_RESOURCE_SHARED
)
742 nobug_resource_error
= "resource hold by another thread nonshared";
747 nobug_resource_error
= "invalid state transistion";
750 if (!nobug_resource_error
)
753 else if (state
== NOBUG_RESOURCE_WAITING
|| state
== NOBUG_RESOURCE_TRYING
)
756 nobug_resource_error
= "invalid state transistion";
758 if (nobug_resource_error
)
765 enum nobug_resource_state
766 nobug_resource_mystate (struct nobug_resource_record
* res
)
768 enum nobug_resource_state ret
= NOBUG_RESOURCE_INVALID
;
770 #if NOBUG_USE_PTHREAD
771 struct nobug_tls_data
* iam
= nobug_thread_get ();
774 LLIST_FOREACH_REV (&res
->users
, u
)
776 struct nobug_resource_user
* user
= (struct nobug_resource_user
*) u
;
777 #if NOBUG_USE_PTHREAD
778 if (user
->thread
== iam
)
784 NOBUG_RESOURCE_UNLOCK
;
791 nobug_resource_dump (struct nobug_resource_record
* resource
, struct nobug_resource_dump_context
* context
)
793 if (!resource
) return;
795 #if NOBUG_USE_PTHREAD
796 nobug_log (context
->flag
, context
->level
,
797 "%.10llu: RESOURCE_DUMP: %s:%d: %s: %s: "
798 "%s %s:%s: hold by %u entities:",
799 ++nobug_counter
, context
->file
, context
->line
, nobug_thread_id_get(), context
->func
,
800 resource
->hdr
.extra
, resource
->type
, resource
->hdr
.name
,
801 llist_count (&resource
->users
));
803 nobug_log (context
->flag
, context
->level
,
804 "%.10llu: RESOURCE_DUMP: %s:%d: %s: "
805 "%s %s:%s: hold by %u entities:",
806 ++nobug_counter
, context
->file
, context
->line
, context
->func
,
807 resource
->hdr
.extra
, resource
->type
, resource
->hdr
.name
,
808 llist_count (&resource
->users
));
811 LLIST_FOREACH (&resource
->users
, n
)
813 struct nobug_resource_user
* node
= (struct nobug_resource_user
*)n
;
814 #if NOBUG_USE_PTHREAD
815 nobug_log (context
->flag
, context
->level
,
816 "%.10llu: RESOURCE_DUMP:"NOBUG_TAB
819 node
->hdr
.extra
, node
->hdr
.name
, node
->thread
->thread_id
,
820 nobug_resource_states
[node
->state
]);
822 nobug_log (context
->flag
, context
->level
,
823 "%.10llu: RESOURCE_DUMP: "NOBUG_TAB
826 node
->hdr
.extra
, node
->hdr
.name
, nobug_resource_states
[node
->state
]);
833 nobug_resource_dump_all (struct nobug_resource_dump_context
* context
)
835 LLIST_FOREACH (&nobug_resource_registry
, n
)
837 struct nobug_resource_record
* node
= (struct nobug_resource_record
*)n
;
838 nobug_resource_dump (node
, context
);
844 nobug_resource_list (struct nobug_resource_dump_context
* context
)
846 #if NOBUG_USE_PTHREAD
847 nobug_log (context
->flag
, context
->level
,
848 "%.10llu: RESOURCE_LIST: %s:%d: %s: %s:",
849 ++nobug_counter
, context
->file
, context
->line
, nobug_thread_id_get(), context
->func
);
851 nobug_log (context
->flag
, context
->level
,
852 "%.10llu: RESOURCE_LIST: %s:%d: %s:",
853 ++nobug_counter
, context
->file
, context
->line
, context
->func
);
856 if (!llist_is_empty (&nobug_resource_registry
))
858 LLIST_FOREACH (&nobug_resource_registry
, n
)
860 struct nobug_resource_record
* node
= (struct nobug_resource_record
*)n
;
861 nobug_log (context
->flag
, context
->level
,
862 "%.10llu: RESOURCE_LIST:"NOBUG_TAB
865 node
->hdr
.extra
, node
->type
, node
->hdr
.name
, node
->object_id
);
870 nobug_log (context
->flag
, context
->level
,
871 "%.10llu: RESOURCE_LIST:"NOBUG_TAB
"No resources registered", ++nobug_counter
);
878 // c-file-style: "gnu"
879 // indent-tabs-mode: nil