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.

auto_explain.c 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /*-------------------------------------------------------------------------
  2. *
  3. * auto_explain.c
  4. *
  5. *
  6. * Copyright (c) 2008-2019, PostgreSQL Global Development Group
  7. *
  8. * IDENTIFICATION
  9. * contrib/auto_explain/auto_explain.c
  10. *
  11. *-------------------------------------------------------------------------
  12. */
  13. #include "postgres.h"
  14. #include <limits.h>
  15. #include "commands/explain.h"
  16. #include "executor/instrument.h"
  17. #include "jit/jit.h"
  18. #include "utils/guc.h"
  19. PG_MODULE_MAGIC;
  20. /* GUC variables */
  21. static int auto_explain_log_min_duration = -1; /* msec or -1 */
  22. static bool auto_explain_log_analyze = false;
  23. static bool auto_explain_log_verbose = false;
  24. static bool auto_explain_log_buffers = false;
  25. static bool auto_explain_log_triggers = false;
  26. static bool auto_explain_log_timing = true;
  27. static int auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
  28. static int auto_explain_log_level = LOG;
  29. static bool auto_explain_log_nested_statements = false;
  30. static double auto_explain_sample_rate = 1;
  31. static const struct config_enum_entry format_options[] = {
  32. {"text", EXPLAIN_FORMAT_TEXT, false},
  33. {"xml", EXPLAIN_FORMAT_XML, false},
  34. {"json", EXPLAIN_FORMAT_JSON, false},
  35. {"yaml", EXPLAIN_FORMAT_YAML, false},
  36. {NULL, 0, false}
  37. };
  38. static const struct config_enum_entry loglevel_options[] = {
  39. {"debug5", DEBUG5, false},
  40. {"debug4", DEBUG4, false},
  41. {"debug3", DEBUG3, false},
  42. {"debug2", DEBUG2, false},
  43. {"debug1", DEBUG1, false},
  44. {"debug", DEBUG2, true},
  45. {"info", INFO, false},
  46. {"notice", NOTICE, false},
  47. {"warning", WARNING, false},
  48. {"log", LOG, false},
  49. {NULL, 0, false}
  50. };
  51. /* Current nesting depth of ExecutorRun calls */
  52. static int nesting_level = 0;
  53. /* Saved hook values in case of unload */
  54. static ExecutorStart_hook_type prev_ExecutorStart = NULL;
  55. static ExecutorRun_hook_type prev_ExecutorRun = NULL;
  56. static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
  57. static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
  58. /* Is the current query sampled, per backend */
  59. static bool current_query_sampled = true;
  60. #define auto_explain_enabled() \
  61. (auto_explain_log_min_duration >= 0 && \
  62. (nesting_level == 0 || auto_explain_log_nested_statements))
  63. void _PG_init(void);
  64. void _PG_fini(void);
  65. static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
  66. static void explain_ExecutorRun(QueryDesc *queryDesc,
  67. ScanDirection direction,
  68. uint64 count, bool execute_once);
  69. static void explain_ExecutorFinish(QueryDesc *queryDesc);
  70. static void explain_ExecutorEnd(QueryDesc *queryDesc);
  71. /*
  72. * Module load callback
  73. */
  74. void
  75. _PG_init(void)
  76. {
  77. /* Define custom GUC variables. */
  78. DefineCustomIntVariable("auto_explain.log_min_duration",
  79. "Sets the minimum execution time above which plans will be logged.",
  80. "Zero prints all plans. -1 turns this feature off.",
  81. &auto_explain_log_min_duration,
  82. -1,
  83. -1, INT_MAX,
  84. PGC_SUSET,
  85. GUC_UNIT_MS,
  86. NULL,
  87. NULL,
  88. NULL);
  89. DefineCustomBoolVariable("auto_explain.log_analyze",
  90. "Use EXPLAIN ANALYZE for plan logging.",
  91. NULL,
  92. &auto_explain_log_analyze,
  93. false,
  94. PGC_SUSET,
  95. 0,
  96. NULL,
  97. NULL,
  98. NULL);
  99. DefineCustomBoolVariable("auto_explain.log_verbose",
  100. "Use EXPLAIN VERBOSE for plan logging.",
  101. NULL,
  102. &auto_explain_log_verbose,
  103. false,
  104. PGC_SUSET,
  105. 0,
  106. NULL,
  107. NULL,
  108. NULL);
  109. DefineCustomBoolVariable("auto_explain.log_buffers",
  110. "Log buffers usage.",
  111. NULL,
  112. &auto_explain_log_buffers,
  113. false,
  114. PGC_SUSET,
  115. 0,
  116. NULL,
  117. NULL,
  118. NULL);
  119. DefineCustomBoolVariable("auto_explain.log_triggers",
  120. "Include trigger statistics in plans.",
  121. "This has no effect unless log_analyze is also set.",
  122. &auto_explain_log_triggers,
  123. false,
  124. PGC_SUSET,
  125. 0,
  126. NULL,
  127. NULL,
  128. NULL);
  129. DefineCustomEnumVariable("auto_explain.log_format",
  130. "EXPLAIN format to be used for plan logging.",
  131. NULL,
  132. &auto_explain_log_format,
  133. EXPLAIN_FORMAT_TEXT,
  134. format_options,
  135. PGC_SUSET,
  136. 0,
  137. NULL,
  138. NULL,
  139. NULL);
  140. DefineCustomEnumVariable("auto_explain.log_level",
  141. "Log level for the plan.",
  142. NULL,
  143. &auto_explain_log_level,
  144. LOG,
  145. loglevel_options,
  146. PGC_SUSET,
  147. 0,
  148. NULL,
  149. NULL,
  150. NULL);
  151. DefineCustomBoolVariable("auto_explain.log_nested_statements",
  152. "Log nested statements.",
  153. NULL,
  154. &auto_explain_log_nested_statements,
  155. false,
  156. PGC_SUSET,
  157. 0,
  158. NULL,
  159. NULL,
  160. NULL);
  161. DefineCustomBoolVariable("auto_explain.log_timing",
  162. "Collect timing data, not just row counts.",
  163. NULL,
  164. &auto_explain_log_timing,
  165. true,
  166. PGC_SUSET,
  167. 0,
  168. NULL,
  169. NULL,
  170. NULL);
  171. DefineCustomRealVariable("auto_explain.sample_rate",
  172. "Fraction of queries to process.",
  173. NULL,
  174. &auto_explain_sample_rate,
  175. 1.0,
  176. 0.0,
  177. 1.0,
  178. PGC_SUSET,
  179. 0,
  180. NULL,
  181. NULL,
  182. NULL);
  183. EmitWarningsOnPlaceholders("auto_explain");
  184. /* Install hooks. */
  185. prev_ExecutorStart = ExecutorStart_hook;
  186. ExecutorStart_hook = explain_ExecutorStart;
  187. prev_ExecutorRun = ExecutorRun_hook;
  188. ExecutorRun_hook = explain_ExecutorRun;
  189. prev_ExecutorFinish = ExecutorFinish_hook;
  190. ExecutorFinish_hook = explain_ExecutorFinish;
  191. prev_ExecutorEnd = ExecutorEnd_hook;
  192. ExecutorEnd_hook = explain_ExecutorEnd;
  193. }
  194. /*
  195. * Module unload callback
  196. */
  197. void
  198. _PG_fini(void)
  199. {
  200. /* Uninstall hooks. */
  201. ExecutorStart_hook = prev_ExecutorStart;
  202. ExecutorRun_hook = prev_ExecutorRun;
  203. ExecutorFinish_hook = prev_ExecutorFinish;
  204. ExecutorEnd_hook = prev_ExecutorEnd;
  205. }
  206. /*
  207. * ExecutorStart hook: start up logging if needed
  208. */
  209. static void
  210. explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
  211. {
  212. /*
  213. * For rate sampling, randomly choose top-level statement. Either all
  214. * nested statements will be explained or none will.
  215. */
  216. if (auto_explain_log_min_duration >= 0 && nesting_level == 0)
  217. current_query_sampled = (random() < auto_explain_sample_rate *
  218. MAX_RANDOM_VALUE);
  219. if (auto_explain_enabled() && current_query_sampled)
  220. {
  221. /* Enable per-node instrumentation iff log_analyze is required. */
  222. if (auto_explain_log_analyze && (eflags & EXEC_FLAG_EXPLAIN_ONLY) == 0)
  223. {
  224. if (auto_explain_log_timing)
  225. queryDesc->instrument_options |= INSTRUMENT_TIMER;
  226. else
  227. queryDesc->instrument_options |= INSTRUMENT_ROWS;
  228. if (auto_explain_log_buffers)
  229. queryDesc->instrument_options |= INSTRUMENT_BUFFERS;
  230. }
  231. }
  232. if (prev_ExecutorStart)
  233. prev_ExecutorStart(queryDesc, eflags);
  234. else
  235. standard_ExecutorStart(queryDesc, eflags);
  236. if (auto_explain_enabled() && current_query_sampled)
  237. {
  238. /*
  239. * Set up to track total elapsed time in ExecutorRun. Make sure the
  240. * space is allocated in the per-query context so it will go away at
  241. * ExecutorEnd.
  242. */
  243. if (queryDesc->totaltime == NULL)
  244. {
  245. MemoryContext oldcxt;
  246. oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
  247. queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL);
  248. MemoryContextSwitchTo(oldcxt);
  249. }
  250. }
  251. }
  252. /*
  253. * ExecutorRun hook: all we need do is track nesting depth
  254. */
  255. static void
  256. explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction,
  257. uint64 count, bool execute_once)
  258. {
  259. nesting_level++;
  260. PG_TRY();
  261. {
  262. if (prev_ExecutorRun)
  263. prev_ExecutorRun(queryDesc, direction, count, execute_once);
  264. else
  265. standard_ExecutorRun(queryDesc, direction, count, execute_once);
  266. nesting_level--;
  267. }
  268. PG_CATCH();
  269. {
  270. nesting_level--;
  271. PG_RE_THROW();
  272. }
  273. PG_END_TRY();
  274. }
  275. /*
  276. * ExecutorFinish hook: all we need do is track nesting depth
  277. */
  278. static void
  279. explain_ExecutorFinish(QueryDesc *queryDesc)
  280. {
  281. nesting_level++;
  282. PG_TRY();
  283. {
  284. if (prev_ExecutorFinish)
  285. prev_ExecutorFinish(queryDesc);
  286. else
  287. standard_ExecutorFinish(queryDesc);
  288. nesting_level--;
  289. }
  290. PG_CATCH();
  291. {
  292. nesting_level--;
  293. PG_RE_THROW();
  294. }
  295. PG_END_TRY();
  296. }
  297. /*
  298. * ExecutorEnd hook: log results if needed
  299. */
  300. static void
  301. explain_ExecutorEnd(QueryDesc *queryDesc)
  302. {
  303. if (queryDesc->totaltime && auto_explain_enabled() && current_query_sampled)
  304. {
  305. double msec;
  306. /*
  307. * Make sure stats accumulation is done. (Note: it's okay if several
  308. * levels of hook all do this.)
  309. */
  310. InstrEndLoop(queryDesc->totaltime);
  311. /* Log plan if duration is exceeded. */
  312. msec = queryDesc->totaltime->total * 1000.0;
  313. if (msec >= auto_explain_log_min_duration)
  314. {
  315. ExplainState *es = NewExplainState();
  316. es->analyze = (queryDesc->instrument_options && auto_explain_log_analyze);
  317. es->verbose = auto_explain_log_verbose;
  318. es->buffers = (es->analyze && auto_explain_log_buffers);
  319. es->timing = (es->analyze && auto_explain_log_timing);
  320. es->summary = es->analyze;
  321. es->format = auto_explain_log_format;
  322. ExplainBeginOutput(es);
  323. ExplainQueryText(es, queryDesc);
  324. ExplainPrintPlan(es, queryDesc);
  325. if (es->analyze && auto_explain_log_triggers)
  326. ExplainPrintTriggers(es, queryDesc);
  327. if (es->costs)
  328. ExplainPrintJITSummary(es, queryDesc);
  329. ExplainEndOutput(es);
  330. /* Remove last line break */
  331. if (es->str->len > 0 && es->str->data[es->str->len - 1] == '\n')
  332. es->str->data[--es->str->len] = '\0';
  333. /* Fix JSON to output an object */
  334. if (auto_explain_log_format == EXPLAIN_FORMAT_JSON)
  335. {
  336. es->str->data[0] = '{';
  337. es->str->data[es->str->len - 1] = '}';
  338. }
  339. /*
  340. * Note: we rely on the existing logging of context or
  341. * debug_query_string to identify just which statement is being
  342. * reported. This isn't ideal but trying to do it here would
  343. * often result in duplication.
  344. */
  345. ereport(auto_explain_log_level,
  346. (errmsg("duration: %.3f ms plan:\n%s",
  347. msec, es->str->data),
  348. errhidestmt(true)));
  349. pfree(es->str->data);
  350. }
  351. }
  352. if (prev_ExecutorEnd)
  353. prev_ExecutorEnd(queryDesc);
  354. else
  355. standard_ExecutorEnd(queryDesc);
  356. }