Mirror of strace – the linux syscall tracer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mmap_cache.c 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
  3. * Copyright (c) 2013-2018 The strace developers.
  4. *
  5. * SPDX-License-Identifier: LGPL-2.1-or-later
  6. */
  7. #include "defs.h"
  8. #include <limits.h>
  9. #include "largefile_wrappers.h"
  10. #include "mmap_cache.h"
  11. #include "mmap_notify.h"
  12. #include "xstring.h"
  13. static unsigned int mmap_cache_generation;
  14. static void
  15. mmap_cache_invalidate(struct tcb *tcp, void *unused)
  16. {
  17. #if SUPPORTED_PERSONALITIES > 1
  18. if (tcp->currpers != DEFAULT_PERSONALITY) {
  19. /* disable stack trace */
  20. return;
  21. }
  22. #endif
  23. mmap_cache_generation++;
  24. debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p",
  25. tcp->mmap_cache ? tcp->mmap_cache->generation : 0,
  26. mmap_cache_generation, tcp,
  27. tcp->mmap_cache ? tcp->mmap_cache->entry : 0);
  28. }
  29. void
  30. mmap_cache_enable(void)
  31. {
  32. static bool use_mmap_cache;
  33. if (!use_mmap_cache) {
  34. mmap_notify_register_client(mmap_cache_invalidate, NULL);
  35. use_mmap_cache = true;
  36. }
  37. }
  38. /* deleting the cache */
  39. static void
  40. delete_mmap_cache(struct tcb *tcp, const char *caller)
  41. {
  42. debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
  43. tcp->mmap_cache ? tcp->mmap_cache->generation : 0,
  44. mmap_cache_generation, tcp,
  45. tcp->mmap_cache ? tcp->mmap_cache->entry : 0, caller);
  46. if (!tcp->mmap_cache)
  47. return;
  48. while (tcp->mmap_cache->size) {
  49. unsigned int i = --tcp->mmap_cache->size;
  50. free(tcp->mmap_cache->entry[i].binary_filename);
  51. tcp->mmap_cache->entry[i].binary_filename = NULL;
  52. }
  53. free(tcp->mmap_cache->entry);
  54. tcp->mmap_cache->entry = NULL;
  55. free(tcp->mmap_cache);
  56. tcp->mmap_cache = NULL;
  57. }
  58. /*
  59. * caching of /proc/ID/maps for each process to speed up stack tracing
  60. *
  61. * The cache must be refreshed after syscalls that affect memory mappings,
  62. * e.g. mmap, mprotect, munmap, execve.
  63. */
  64. extern enum mmap_cache_rebuild_result
  65. mmap_cache_rebuild_if_invalid(struct tcb *tcp, const char *caller)
  66. {
  67. if (tcp->mmap_cache
  68. && tcp->mmap_cache->generation != mmap_cache_generation)
  69. delete_mmap_cache(tcp, caller);
  70. if (tcp->mmap_cache)
  71. return MMAP_CACHE_REBUILD_READY;
  72. char filename[sizeof("/proc/4294967296/maps")];
  73. xsprintf(filename, "/proc/%u/maps", tcp->pid);
  74. FILE *fp = fopen_stream(filename, "r");
  75. if (!fp) {
  76. perror_msg("fopen: %s", filename);
  77. return MMAP_CACHE_REBUILD_NOCACHE;
  78. }
  79. struct mmap_cache_t cache = {
  80. .free_fn = delete_mmap_cache,
  81. .generation = mmap_cache_generation
  82. };
  83. /* start with a small dynamically-allocated array and then expand it */
  84. size_t allocated = 0;
  85. char buffer[PATH_MAX + 80];
  86. while (fgets(buffer, sizeof(buffer), fp) != NULL) {
  87. unsigned long start_addr, end_addr, mmap_offset;
  88. char read_bit;
  89. char write_bit;
  90. char exec_bit;
  91. char shared_bit;
  92. unsigned long major, minor;
  93. char binary_path[sizeof(buffer)];
  94. if (sscanf(buffer, "%lx-%lx %c%c%c%c %lx %lx:%lx %*d %[^\n]",
  95. &start_addr, &end_addr,
  96. &read_bit, &write_bit, &exec_bit, &shared_bit,
  97. &mmap_offset,
  98. &major, &minor,
  99. binary_path) != 10)
  100. continue;
  101. /* skip mappings that have unknown protection */
  102. if (!(read_bit == '-' || read_bit == 'r'))
  103. continue;
  104. if (!(write_bit == '-' || write_bit == 'w'))
  105. continue;
  106. if (!(exec_bit == '-' || exec_bit == 'x'))
  107. continue;
  108. if (!(shared_bit == 'p' || shared_bit == 's'))
  109. continue;
  110. if (end_addr < start_addr) {
  111. error_msg("%s: unrecognized file format", filename);
  112. break;
  113. }
  114. struct mmap_cache_entry_t *entry;
  115. /*
  116. * sanity check to make sure that we're storing
  117. * non-overlapping regions in ascending order
  118. */
  119. if (cache.size > 0) {
  120. entry = &cache.entry[cache.size - 1];
  121. if (entry->start_addr == start_addr &&
  122. entry->end_addr == end_addr) {
  123. /* duplicate entry, e.g. [vsyscall] */
  124. continue;
  125. }
  126. if (start_addr <= entry->start_addr ||
  127. start_addr < entry->end_addr) {
  128. debug_msg("%s: overlapping memory region: "
  129. "\"%s\" [%08lx-%08lx] overlaps with "
  130. "\"%s\" [%08lx-%08lx]",
  131. filename, binary_path, start_addr,
  132. end_addr, entry->binary_filename,
  133. entry->start_addr, entry->end_addr);
  134. continue;
  135. }
  136. }
  137. if (cache.size >= allocated)
  138. cache.entry = xgrowarray(cache.entry, &allocated,
  139. sizeof(*cache.entry));
  140. entry = &cache.entry[cache.size];
  141. entry->start_addr = start_addr;
  142. entry->end_addr = end_addr;
  143. entry->mmap_offset = mmap_offset;
  144. entry->protections = (
  145. 0
  146. | ((read_bit == 'r')? MMAP_CACHE_PROT_READABLE : 0)
  147. | ((write_bit == 'w')? MMAP_CACHE_PROT_WRITABLE : 0)
  148. | ((exec_bit == 'x')? MMAP_CACHE_PROT_EXECUTABLE: 0)
  149. | ((shared_bit == 's')? MMAP_CACHE_PROT_SHARED : 0)
  150. );
  151. entry->major = major;
  152. entry->minor = minor;
  153. entry->binary_filename = xstrdup(binary_path);
  154. cache.size++;
  155. }
  156. fclose(fp);
  157. if (!cache.size)
  158. return MMAP_CACHE_REBUILD_NOCACHE;
  159. tcp->mmap_cache = xmalloc(sizeof(*tcp->mmap_cache));
  160. memcpy(tcp->mmap_cache, &cache, sizeof(cache));
  161. debug_func_msg("tgen=%u, ggen=%u, tcp=%p, cache=%p, caller=%s",
  162. tcp->mmap_cache->generation, mmap_cache_generation,
  163. tcp, tcp->mmap_cache->entry, caller);
  164. return MMAP_CACHE_REBUILD_RENEWED;
  165. }
  166. struct mmap_cache_entry_t *
  167. mmap_cache_search(struct tcb *tcp, unsigned long ip)
  168. {
  169. if (!tcp->mmap_cache)
  170. return NULL;
  171. int lower = 0;
  172. int upper = (int) tcp->mmap_cache->size - 1;
  173. while (lower <= upper) {
  174. int mid = (upper + lower) / 2;
  175. struct mmap_cache_entry_t *entry = &tcp->mmap_cache->entry[mid];
  176. if (ip >= entry->start_addr &&
  177. ip < entry->end_addr)
  178. return entry;
  179. else if (ip < entry->start_addr)
  180. upper = mid - 1;
  181. else
  182. lower = mid + 1;
  183. }
  184. return NULL;
  185. }
  186. struct mmap_cache_entry_t *
  187. mmap_cache_search_custom(struct tcb *tcp, mmap_cache_search_fn fn, void *data)
  188. {
  189. for (unsigned int i = 0; i < tcp->mmap_cache->size; i++) {
  190. if (fn(tcp->mmap_cache->entry + i, data))
  191. return tcp->mmap_cache->entry + i;
  192. }
  193. return NULL;
  194. }