xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_thread_history.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- sanitizer_thread_history.cpp --------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "sanitizer_thread_history.h"
10 
11 #include "sanitizer_stackdepot.h"
12 namespace __sanitizer {
13 
PrintThreadHistory(ThreadRegistry & registry,InternalScopedString & out)14 void PrintThreadHistory(ThreadRegistry &registry, InternalScopedString &out) {
15   ThreadRegistryLock l(&registry);
16   // Stack traces are largest part of printout and they often the same for
17   // multiple threads, so we will deduplicate them.
18   InternalMmapVector<const ThreadContextBase *> stacks;
19 
20   registry.RunCallbackForEachThreadLocked(
21       [](ThreadContextBase *context, void *arg) {
22         static_cast<decltype(&stacks)>(arg)->push_back(context);
23       },
24       &stacks);
25 
26   Sort(stacks.data(), stacks.size(),
27        [](const ThreadContextBase *a, const ThreadContextBase *b) {
28          if (a->stack_id < b->stack_id)
29            return true;
30          if (a->stack_id > b->stack_id)
31            return false;
32          return a->unique_id < b->unique_id;
33        });
34 
35   auto describe_thread = [&](const ThreadContextBase *context) {
36     if (!context) {
37       out.Append("T-1");
38       return;
39     }
40     out.AppendF("T%llu/%llu", context->unique_id, context->os_id);
41     if (internal_strlen(context->name))
42       out.AppendF(" (%s)", context->name);
43   };
44 
45   auto get_parent =
46       [&](const ThreadContextBase *context) -> const ThreadContextBase * {
47     if (!context)
48       return nullptr;
49     ThreadContextBase *parent = registry.GetThreadLocked(context->parent_tid);
50     if (!parent)
51       return nullptr;
52     if (parent->unique_id >= context->unique_id)
53       return nullptr;
54     return parent;
55   };
56 
57   const ThreadContextBase *prev = nullptr;
58   for (const ThreadContextBase *context : stacks) {
59     if (prev && prev->stack_id != context->stack_id)
60       StackDepotGet(prev->stack_id).PrintTo(&out);
61     prev = context;
62     out.Append("Thread ");
63     describe_thread(context);
64     out.Append(" was created by ");
65     describe_thread(get_parent(context));
66     out.Append("\n");
67   }
68   if (prev)
69     StackDepotGet(prev->stack_id).PrintTo(&out);
70 }
71 
72 }  // namespace __sanitizer
73