aboutsummaryrefslogtreecommitdiff
path: root/compiler-rt/lib/ctx_profile/RootAutoDetector.h
blob: 2e0307ca03c7fc07e6a246ce2dc054f2c34fa360 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*===- RootAutodetector.h- auto-detect roots for ctxprof  -----------------===*\
|*
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|* See https://llvm.org/LICENSE.txt for license information.
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|*
\*===----------------------------------------------------------------------===*/

#ifndef CTX_PROFILE_ROOTAUTODETECTOR_H_
#define CTX_PROFILE_ROOTAUTODETECTOR_H_

#include "sanitizer_common/sanitizer_dense_map.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_vector.h"
#include <pthread.h>
#include <sanitizer/common_interface_defs.h>

using namespace __asan;
using namespace __sanitizer;

namespace __ctx_profile {

/// Capture all the stack traces observed for a specific thread. The "for a
/// specific thread" part is not enforced, but assumed in determineRoots.
class PerThreadCallsiteTrie {
protected:
  /// A trie. A node is the address of a callsite in a function activation. A
  /// child is a callsite in the activation made from the callsite
  /// corresponding to the parent.
  struct Trie final {
    const uptr CallsiteAddress;
    uint64_t Count = 0;
    DenseMap<uptr, Trie> Children;

    Trie(uptr CallsiteAddress = 0) : CallsiteAddress(CallsiteAddress) {}
  };
  Trie TheTrie;

  /// Return the runtime start address of the function that contains the call at
  /// the runtime address CallsiteAddress. May be overriden for easy testing.
  virtual uptr getFctStartAddr(uptr CallsiteAddress) const;

public:
  PerThreadCallsiteTrie(const PerThreadCallsiteTrie &) = delete;
  PerThreadCallsiteTrie(PerThreadCallsiteTrie &&) = default;
  PerThreadCallsiteTrie() = default;

  virtual ~PerThreadCallsiteTrie() = default;

  void insertStack(const StackTrace &ST);

  /// Return the runtime address of root functions, as determined for this
  /// thread, together with the number of samples that included them.
  DenseMap<uptr, uint64_t> determineRoots() const;
};

class RootAutoDetector final {
  // A prime number. We may want to make this configurable at collection start.
  static const uint64_t SampleRate = 6113;
  const unsigned WaitSeconds;
  pthread_t WorkerThread;

  struct PerThreadSamples {
    PerThreadSamples(RootAutoDetector &Parent);

    PerThreadCallsiteTrie TrieRoot;
    SpinMutex M;
  };
  SpinMutex AllSamplesMutex;
  SANITIZER_GUARDED_BY(AllSamplesMutex)
  Vector<PerThreadSamples *> AllSamples;
  atomic_uintptr_t &FunctionDataListHead;
  atomic_uintptr_t &Self;
  void collectStack();

public:
  RootAutoDetector(atomic_uintptr_t &FunctionDataListHead,
                   atomic_uintptr_t &Self, unsigned WaitSeconds)
      : WaitSeconds(WaitSeconds), FunctionDataListHead(FunctionDataListHead),
        Self(Self) {}

  // Samples the stack at `SampleRate` (rate observed independently on each
  // thread) in thread local `PerThreadCallsiteTrie`s.
  void sample();

  // Start a thread waiting `WaitSeconds`, after which it uses the
  // `PerThreadCallsiteTrie` data observed so far over all threads to determine
  // roots. Marks those roots by traversing the linked list of FunctionData that
  // starts at `FunctionDataListHead`, and assigning their `CtxRoot`. Finally,
  // resets the `Self` atomic, so that other threads don't continue calling
  // `sample`.
  void start();

  // join the waiting thread.
  void join();
};

} // namespace __ctx_profile
#endif