PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/trace.c

https://gitlab.com/oyvholm/git
C | 449 lines | 361 code | 53 blank | 35 comment | 33 complexity | 56362ade35ca3ddc5234247eeba352ea MD5 | raw file
  1. /*
  2. * GIT - The information manager from hell
  3. *
  4. * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
  5. * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
  6. * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
  7. * Copyright (C) 2006 Mike McCormack
  8. * Copyright (C) 2006 Christian Couder
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23. */
  24. #include "cache.h"
  25. #include "quote.h"
  26. /*
  27. * "Normalize" a key argument by converting NULL to our trace_default,
  28. * and otherwise passing through the value. All caller-facing functions
  29. * should normalize their inputs in this way, though most get it
  30. * for free by calling get_trace_fd() (directly or indirectly).
  31. */
  32. static void normalize_trace_key(struct trace_key **key)
  33. {
  34. static struct trace_key trace_default = { "GIT_TRACE" };
  35. if (!*key)
  36. *key = &trace_default;
  37. }
  38. /* Get a trace file descriptor from "key" env variable. */
  39. static int get_trace_fd(struct trace_key *key)
  40. {
  41. const char *trace;
  42. normalize_trace_key(&key);
  43. /* don't open twice */
  44. if (key->initialized)
  45. return key->fd;
  46. trace = getenv(key->key);
  47. if (!trace || !strcmp(trace, "") ||
  48. !strcmp(trace, "0") || !strcasecmp(trace, "false"))
  49. key->fd = 0;
  50. else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
  51. key->fd = STDERR_FILENO;
  52. else if (strlen(trace) == 1 && isdigit(*trace))
  53. key->fd = atoi(trace);
  54. else if (is_absolute_path(trace)) {
  55. int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
  56. if (fd == -1) {
  57. warning("could not open '%s' for tracing: %s",
  58. trace, strerror(errno));
  59. trace_disable(key);
  60. } else {
  61. key->fd = fd;
  62. key->need_close = 1;
  63. }
  64. } else {
  65. warning("unknown trace value for '%s': %s\n"
  66. " If you want to trace into a file, then please set %s\n"
  67. " to an absolute pathname (starting with /)",
  68. key->key, trace, key->key);
  69. trace_disable(key);
  70. }
  71. key->initialized = 1;
  72. return key->fd;
  73. }
  74. void trace_disable(struct trace_key *key)
  75. {
  76. normalize_trace_key(&key);
  77. if (key->need_close)
  78. close(key->fd);
  79. key->fd = 0;
  80. key->initialized = 1;
  81. key->need_close = 0;
  82. }
  83. static int prepare_trace_line(const char *file, int line,
  84. struct trace_key *key, struct strbuf *buf)
  85. {
  86. static struct trace_key trace_bare = TRACE_KEY_INIT(BARE);
  87. struct timeval tv;
  88. struct tm tm;
  89. time_t secs;
  90. if (!trace_want(key))
  91. return 0;
  92. set_try_to_free_routine(NULL); /* is never reset */
  93. /* unit tests may want to disable additional trace output */
  94. if (trace_want(&trace_bare))
  95. return 1;
  96. /* print current timestamp */
  97. gettimeofday(&tv, NULL);
  98. secs = tv.tv_sec;
  99. localtime_r(&secs, &tm);
  100. strbuf_addf(buf, "%02d:%02d:%02d.%06ld ", tm.tm_hour, tm.tm_min,
  101. tm.tm_sec, (long) tv.tv_usec);
  102. #ifdef HAVE_VARIADIC_MACROS
  103. /* print file:line */
  104. strbuf_addf(buf, "%s:%d ", file, line);
  105. /* align trace output (column 40 catches most files names in git) */
  106. while (buf->len < 40)
  107. strbuf_addch(buf, ' ');
  108. #endif
  109. return 1;
  110. }
  111. static void trace_write(struct trace_key *key, const void *buf, unsigned len)
  112. {
  113. if (write_in_full(get_trace_fd(key), buf, len) < 0) {
  114. normalize_trace_key(&key);
  115. warning("unable to write trace for %s: %s",
  116. key->key, strerror(errno));
  117. trace_disable(key);
  118. }
  119. }
  120. void trace_verbatim(struct trace_key *key, const void *buf, unsigned len)
  121. {
  122. if (!trace_want(key))
  123. return;
  124. trace_write(key, buf, len);
  125. }
  126. static void print_trace_line(struct trace_key *key, struct strbuf *buf)
  127. {
  128. strbuf_complete_line(buf);
  129. trace_write(key, buf->buf, buf->len);
  130. strbuf_release(buf);
  131. }
  132. static void trace_vprintf_fl(const char *file, int line, struct trace_key *key,
  133. const char *format, va_list ap)
  134. {
  135. struct strbuf buf = STRBUF_INIT;
  136. if (!prepare_trace_line(file, line, key, &buf))
  137. return;
  138. strbuf_vaddf(&buf, format, ap);
  139. print_trace_line(key, &buf);
  140. }
  141. static void trace_argv_vprintf_fl(const char *file, int line,
  142. const char **argv, const char *format,
  143. va_list ap)
  144. {
  145. struct strbuf buf = STRBUF_INIT;
  146. if (!prepare_trace_line(file, line, NULL, &buf))
  147. return;
  148. strbuf_vaddf(&buf, format, ap);
  149. sq_quote_argv(&buf, argv, 0);
  150. print_trace_line(NULL, &buf);
  151. }
  152. void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
  153. const struct strbuf *data)
  154. {
  155. struct strbuf buf = STRBUF_INIT;
  156. if (!prepare_trace_line(file, line, key, &buf))
  157. return;
  158. strbuf_addbuf(&buf, data);
  159. print_trace_line(key, &buf);
  160. }
  161. static struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
  162. static void trace_performance_vprintf_fl(const char *file, int line,
  163. uint64_t nanos, const char *format,
  164. va_list ap)
  165. {
  166. struct strbuf buf = STRBUF_INIT;
  167. if (!prepare_trace_line(file, line, &trace_perf_key, &buf))
  168. return;
  169. strbuf_addf(&buf, "performance: %.9f s", (double) nanos / 1000000000);
  170. if (format && *format) {
  171. strbuf_addstr(&buf, ": ");
  172. strbuf_vaddf(&buf, format, ap);
  173. }
  174. print_trace_line(&trace_perf_key, &buf);
  175. }
  176. #ifndef HAVE_VARIADIC_MACROS
  177. void trace_printf(const char *format, ...)
  178. {
  179. va_list ap;
  180. va_start(ap, format);
  181. trace_vprintf_fl(NULL, 0, NULL, format, ap);
  182. va_end(ap);
  183. }
  184. void trace_printf_key(struct trace_key *key, const char *format, ...)
  185. {
  186. va_list ap;
  187. va_start(ap, format);
  188. trace_vprintf_fl(NULL, 0, key, format, ap);
  189. va_end(ap);
  190. }
  191. void trace_argv_printf(const char **argv, const char *format, ...)
  192. {
  193. va_list ap;
  194. va_start(ap, format);
  195. trace_argv_vprintf_fl(NULL, 0, argv, format, ap);
  196. va_end(ap);
  197. }
  198. void trace_strbuf(struct trace_key *key, const struct strbuf *data)
  199. {
  200. trace_strbuf_fl(NULL, 0, key, data);
  201. }
  202. void trace_performance(uint64_t nanos, const char *format, ...)
  203. {
  204. va_list ap;
  205. va_start(ap, format);
  206. trace_performance_vprintf_fl(NULL, 0, nanos, format, ap);
  207. va_end(ap);
  208. }
  209. void trace_performance_since(uint64_t start, const char *format, ...)
  210. {
  211. va_list ap;
  212. va_start(ap, format);
  213. trace_performance_vprintf_fl(NULL, 0, getnanotime() - start,
  214. format, ap);
  215. va_end(ap);
  216. }
  217. #else
  218. void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
  219. const char *format, ...)
  220. {
  221. va_list ap;
  222. va_start(ap, format);
  223. trace_vprintf_fl(file, line, key, format, ap);
  224. va_end(ap);
  225. }
  226. void trace_argv_printf_fl(const char *file, int line, const char **argv,
  227. const char *format, ...)
  228. {
  229. va_list ap;
  230. va_start(ap, format);
  231. trace_argv_vprintf_fl(file, line, argv, format, ap);
  232. va_end(ap);
  233. }
  234. void trace_performance_fl(const char *file, int line, uint64_t nanos,
  235. const char *format, ...)
  236. {
  237. va_list ap;
  238. va_start(ap, format);
  239. trace_performance_vprintf_fl(file, line, nanos, format, ap);
  240. va_end(ap);
  241. }
  242. #endif /* HAVE_VARIADIC_MACROS */
  243. static const char *quote_crnl(const char *path)
  244. {
  245. static struct strbuf new_path = STRBUF_INIT;
  246. if (!path)
  247. return NULL;
  248. strbuf_reset(&new_path);
  249. while (*path) {
  250. switch (*path) {
  251. case '\\': strbuf_addstr(&new_path, "\\\\"); break;
  252. case '\n': strbuf_addstr(&new_path, "\\n"); break;
  253. case '\r': strbuf_addstr(&new_path, "\\r"); break;
  254. default:
  255. strbuf_addch(&new_path, *path);
  256. }
  257. path++;
  258. }
  259. return new_path.buf;
  260. }
  261. /* FIXME: move prefix to startup_info struct and get rid of this arg */
  262. void trace_repo_setup(const char *prefix)
  263. {
  264. static struct trace_key key = TRACE_KEY_INIT(SETUP);
  265. const char *git_work_tree;
  266. char *cwd;
  267. if (!trace_want(&key))
  268. return;
  269. cwd = xgetcwd();
  270. if (!(git_work_tree = get_git_work_tree()))
  271. git_work_tree = "(null)";
  272. if (!prefix)
  273. prefix = "(null)";
  274. trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
  275. trace_printf_key(&key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_dir()));
  276. trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
  277. trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
  278. trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
  279. free(cwd);
  280. }
  281. int trace_want(struct trace_key *key)
  282. {
  283. return !!get_trace_fd(key);
  284. }
  285. #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
  286. static inline uint64_t highres_nanos(void)
  287. {
  288. struct timespec ts;
  289. if (clock_gettime(CLOCK_MONOTONIC, &ts))
  290. return 0;
  291. return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
  292. }
  293. #elif defined (GIT_WINDOWS_NATIVE)
  294. static inline uint64_t highres_nanos(void)
  295. {
  296. static uint64_t high_ns, scaled_low_ns;
  297. static int scale;
  298. LARGE_INTEGER cnt;
  299. if (!scale) {
  300. if (!QueryPerformanceFrequency(&cnt))
  301. return 0;
  302. /* high_ns = number of ns per cnt.HighPart */
  303. high_ns = (1000000000LL << 32) / (uint64_t) cnt.QuadPart;
  304. /*
  305. * Number of ns per cnt.LowPart is 10^9 / frequency (or
  306. * high_ns >> 32). For maximum precision, we scale this factor
  307. * so that it just fits within 32 bit (i.e. won't overflow if
  308. * multiplied with cnt.LowPart).
  309. */
  310. scaled_low_ns = high_ns;
  311. scale = 32;
  312. while (scaled_low_ns >= 0x100000000LL) {
  313. scaled_low_ns >>= 1;
  314. scale--;
  315. }
  316. }
  317. /* if QPF worked on initialization, we expect QPC to work as well */
  318. QueryPerformanceCounter(&cnt);
  319. return (high_ns * cnt.HighPart) +
  320. ((scaled_low_ns * cnt.LowPart) >> scale);
  321. }
  322. #else
  323. # define highres_nanos() 0
  324. #endif
  325. static inline uint64_t gettimeofday_nanos(void)
  326. {
  327. struct timeval tv;
  328. gettimeofday(&tv, NULL);
  329. return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
  330. }
  331. /*
  332. * Returns nanoseconds since the epoch (01/01/1970), for performance tracing
  333. * (i.e. favoring high precision over wall clock time accuracy).
  334. */
  335. uint64_t getnanotime(void)
  336. {
  337. static uint64_t offset;
  338. if (offset > 1) {
  339. /* initialization succeeded, return offset + high res time */
  340. return offset + highres_nanos();
  341. } else if (offset == 1) {
  342. /* initialization failed, fall back to gettimeofday */
  343. return gettimeofday_nanos();
  344. } else {
  345. /* initialize offset if high resolution timer works */
  346. uint64_t now = gettimeofday_nanos();
  347. uint64_t highres = highres_nanos();
  348. if (highres)
  349. offset = now - highres;
  350. else
  351. offset = 1;
  352. return now;
  353. }
  354. }
  355. static uint64_t command_start_time;
  356. static struct strbuf command_line = STRBUF_INIT;
  357. static void print_command_performance_atexit(void)
  358. {
  359. trace_performance_since(command_start_time, "git command:%s",
  360. command_line.buf);
  361. }
  362. void trace_command_performance(const char **argv)
  363. {
  364. if (!trace_want(&trace_perf_key))
  365. return;
  366. if (!command_start_time)
  367. atexit(print_command_performance_atexit);
  368. strbuf_reset(&command_line);
  369. sq_quote_argv(&command_line, argv, 0);
  370. command_start_time = getnanotime();
  371. }