aboutsummaryrefslogtreecommitdiff
path: root/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
blob: 6b5e91fbc0183830ebfc10642e2e5a4cbd922ae6 (plain) (blame)
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//===-- sanitizer_coverage_mapping.cc -------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Mmap-based implementation of sanitizer coverage.
//
// This is part of the implementation of code coverage that does not require
// __sanitizer_cov_dump() call. Data is stored in 2 files per process.
//
// $pid.sancov.map describes process memory layout in the following text-based
// format:
// <pointer size in bits>  // 1 line, 32 or 64
// <mapping start> <mapping end> <base address> <dso name> // repeated
// ...
// Mapping lines are NOT sorted. This file is updated every time memory layout
// is changed (i.e. in dlopen() and dlclose() interceptors).
//
// $pid.sancov.raw is a binary dump of PC values, sizeof(uptr) each. Again, not
// sorted. This file is extended by 64Kb at a time and mapped into memory. It
// contains one or more 0 words at the end, up to the next 64Kb aligned offset.
//
// To convert these 2 files to the usual .sancov format, run sancov.py rawunpack
// $pid.sancov.raw.
//
//===----------------------------------------------------------------------===//

#include "sanitizer_allocator_internal.h"
#include "sanitizer_libc.h"
#include "sanitizer_procmaps.h"

namespace __sanitizer {

static const uptr kMaxNumberOfModules = 1 << 14;
static const uptr kMaxTextSize = 64 * 1024;

struct CachedMapping {
 public:
  bool NeedsUpdate(uptr pc) {
    int new_pid = internal_getpid();
    if (last_pid == new_pid && pc && pc >= last_range_start &&
        pc < last_range_end)
      return false;
    last_pid = new_pid;
    return true;
  }

  void SetModuleRange(uptr start, uptr end) {
    last_range_start = start;
    last_range_end = end;
  }

 private:
  uptr last_range_start, last_range_end;
  int last_pid;
};

static CachedMapping cached_mapping;
static StaticSpinMutex mapping_mu;

void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) {
  if (!common_flags()->coverage_direct) return;

  SpinMutexLock l(&mapping_mu);

  if (!cached_mapping.NeedsUpdate(caller_pc))
    return;

  InternalScopedString text(kMaxTextSize);

  {
    InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
    CHECK(modules.data());
    int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules,
                                     /* filter */ 0);

    text.append("%d\n", sizeof(uptr) * 8);
    for (int i = 0; i < n_modules; ++i) {
      const char *module_name = StripModuleName(modules[i].full_name());
      uptr base = modules[i].base_address();
      for (auto iter = modules[i].ranges(); iter.hasNext();) {
        const auto *range = iter.next();
        if (range->executable) {
          uptr start = range->beg;
          uptr end = range->end;
          text.append("%zx %zx %zx %s\n", start, end, base, module_name);
          if (caller_pc && caller_pc >= start && caller_pc < end)
            cached_mapping.SetModuleRange(start, end);
        }
      }
      modules[i].clear();
    }
  }

  int err;
  InternalScopedString tmp_path(64 + internal_strlen(coverage_dir));
  uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(),
                               "%s/%zd.sancov.map.tmp", coverage_dir,
                               internal_getpid());
  CHECK_LE(res, tmp_path.size());
  uptr map_fd = OpenFile(tmp_path.data(), true);
  if (internal_iserror(map_fd, &err)) {
    Report(" Coverage: failed to open %s for writing: %d\n", tmp_path.data(),
           err);
    Die();
  }

  res = internal_write(map_fd, text.data(), text.length());
  if (internal_iserror(res, &err)) {
    Printf("sancov.map write failed: %d\n", err);
    Die();
  }
  internal_close(map_fd);

  InternalScopedString path(64 + internal_strlen(coverage_dir));
  res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map",
                          coverage_dir, internal_getpid());
  CHECK_LE(res, path.size());
  res = internal_rename(tmp_path.data(), path.data());
  if (internal_iserror(res, &err)) {
    Printf("sancov.map rename failed: %d\n", err);
    Die();
  }
}

}  // namespace __sanitizer