|
21 | 21 |
|
22 | 22 | #include "postgres.h"
|
23 | 23 |
|
| 24 | +#include "funcapi.h" |
24 | 25 | #include "mb/pg_wchar.h"
|
25 | 26 | #include "miscadmin.h"
|
| 27 | +#include "utils/builtins.h" |
26 | 28 | #include "utils/memdebug.h"
|
27 | 29 | #include "utils/memutils.h"
|
28 | 30 |
|
@@ -67,6 +69,12 @@ static void MemoryContextStatsPrint(MemoryContext context, void *passthru,
|
67 | 69 | #define AssertNotInCriticalSection(context) \
|
68 | 70 | Assert(CritSectionCount == 0 || (context)->allowInCritSection)
|
69 | 71 |
|
| 72 | +/* ---------- |
| 73 | + * The max bytes for showing identifiers of MemoryContext. |
| 74 | + * ---------- |
| 75 | + */ |
| 76 | +#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024 |
| 77 | + |
70 | 78 | /*****************************************************************************
|
71 | 79 | * EXPORTED ROUTINES *
|
72 | 80 | *****************************************************************************/
|
@@ -1220,3 +1228,133 @@ pchomp(const char *in)
|
1220 | 1228 | n--;
|
1221 | 1229 | return pnstrdup(in, n);
|
1222 | 1230 | }
|
| 1231 | + |
| 1232 | +/* |
| 1233 | + * PutMemoryContextsStatsTupleStore |
| 1234 | + * One recursion level for pg_get_backend_memory_contexts. |
| 1235 | + */ |
| 1236 | +static void |
| 1237 | +PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore, |
| 1238 | + TupleDesc tupdesc, MemoryContext context, |
| 1239 | + const char *parent, int level) |
| 1240 | +{ |
| 1241 | +#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 9 |
| 1242 | + |
| 1243 | + Datum values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS]; |
| 1244 | + bool nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS]; |
| 1245 | + MemoryContextCounters stat; |
| 1246 | + MemoryContext child; |
| 1247 | + const char *name; |
| 1248 | + const char *ident; |
| 1249 | + |
| 1250 | + AssertArg(MemoryContextIsValid(context)); |
| 1251 | + |
| 1252 | + name = context->name; |
| 1253 | + ident = context->ident; |
| 1254 | + |
| 1255 | + /* |
| 1256 | + * To be consistent with logging output, we label dynahash contexts |
| 1257 | + * with just the hash table name as with MemoryContextStatsPrint(). |
| 1258 | + */ |
| 1259 | + if (ident && strcmp(name, "dynahash") == 0) |
| 1260 | + { |
| 1261 | + name = ident; |
| 1262 | + ident = NULL; |
| 1263 | + } |
| 1264 | + |
| 1265 | + /* Examine the context itself */ |
| 1266 | + memset(&stat, 0, sizeof(stat)); |
| 1267 | + (*context->methods->stats) (context, NULL, (void *) &level, &stat); |
| 1268 | + |
| 1269 | + memset(values, 0, sizeof(values)); |
| 1270 | + memset(nulls, 0, sizeof(nulls)); |
| 1271 | + |
| 1272 | + if (name) |
| 1273 | + values[0] = CStringGetTextDatum(name); |
| 1274 | + else |
| 1275 | + nulls[0] = true; |
| 1276 | + |
| 1277 | + if (ident) |
| 1278 | + { |
| 1279 | + int idlen = strlen(ident); |
| 1280 | + char clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE]; |
| 1281 | + |
| 1282 | + /* |
| 1283 | + * Some identifiers such as SQL query string can be very long, |
| 1284 | + * truncate oversize identifiers. |
| 1285 | + */ |
| 1286 | + if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE) |
| 1287 | + idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1); |
| 1288 | + |
| 1289 | + memcpy(clipped_ident, ident, idlen); |
| 1290 | + clipped_ident[idlen] = '\0'; |
| 1291 | + values[1] = CStringGetTextDatum(clipped_ident); |
| 1292 | + } |
| 1293 | + else |
| 1294 | + nulls[1] = true; |
| 1295 | + |
| 1296 | + if (parent) |
| 1297 | + values[2] = CStringGetTextDatum(parent); |
| 1298 | + else |
| 1299 | + nulls[2] = true; |
| 1300 | + |
| 1301 | + values[3] = Int32GetDatum(level); |
| 1302 | + values[4] = Int64GetDatum(stat.totalspace); |
| 1303 | + values[5] = Int64GetDatum(stat.nblocks); |
| 1304 | + values[6] = Int64GetDatum(stat.freespace); |
| 1305 | + values[7] = Int64GetDatum(stat.freechunks); |
| 1306 | + values[8] = Int64GetDatum(stat.totalspace - stat.freespace); |
| 1307 | + tuplestore_putvalues(tupstore, tupdesc, values, nulls); |
| 1308 | + |
| 1309 | + for (child = context->firstchild; child != NULL; child = child->nextchild) |
| 1310 | + { |
| 1311 | + PutMemoryContextsStatsTupleStore(tupstore, tupdesc, |
| 1312 | + child, name, level + 1); |
| 1313 | + } |
| 1314 | +} |
| 1315 | + |
| 1316 | +/* |
| 1317 | + * pg_get_backend_memory_contexts |
| 1318 | + * SQL SRF showing backend memory context. |
| 1319 | + */ |
| 1320 | +Datum |
| 1321 | +pg_get_backend_memory_contexts(PG_FUNCTION_ARGS) |
| 1322 | +{ |
| 1323 | + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; |
| 1324 | + TupleDesc tupdesc; |
| 1325 | + Tuplestorestate *tupstore; |
| 1326 | + MemoryContext per_query_ctx; |
| 1327 | + MemoryContext oldcontext; |
| 1328 | + |
| 1329 | + /* check to see if caller supports us returning a tuplestore */ |
| 1330 | + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) |
| 1331 | + ereport(ERROR, |
| 1332 | + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 1333 | + errmsg("set-valued function called in context that cannot accept a set"))); |
| 1334 | + if (!(rsinfo->allowedModes & SFRM_Materialize)) |
| 1335 | + ereport(ERROR, |
| 1336 | + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 1337 | + errmsg("materialize mode required, but it is not allowed in this context"))); |
| 1338 | + |
| 1339 | + /* Build a tuple descriptor for our result type */ |
| 1340 | + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) |
| 1341 | + elog(ERROR, "return type must be a row type"); |
| 1342 | + |
| 1343 | + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; |
| 1344 | + oldcontext = MemoryContextSwitchTo(per_query_ctx); |
| 1345 | + |
| 1346 | + tupstore = tuplestore_begin_heap(true, false, work_mem); |
| 1347 | + rsinfo->returnMode = SFRM_Materialize; |
| 1348 | + rsinfo->setResult = tupstore; |
| 1349 | + rsinfo->setDesc = tupdesc; |
| 1350 | + |
| 1351 | + MemoryContextSwitchTo(oldcontext); |
| 1352 | + |
| 1353 | + PutMemoryContextsStatsTupleStore(tupstore, tupdesc, |
| 1354 | + TopMemoryContext, NULL, 0); |
| 1355 | + |
| 1356 | + /* clean up and return the tuplestore */ |
| 1357 | + tuplestore_donestoring(tupstore); |
| 1358 | + |
| 1359 | + return (Datum) 0; |
| 1360 | +} |
0 commit comments