19
19
#include "mb/pg_wchar.h"
20
20
#include "storage/proc.h"
21
21
#include "storage/procarray.h"
22
+ #include "utils/array.h"
22
23
#include "utils/builtins.h"
24
+ #include "utils/hsearch.h"
23
25
24
26
/* ----------
25
27
* The max bytes for showing identifiers of MemoryContext.
26
28
* ----------
27
29
*/
28
30
#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024
29
31
32
+ /*
33
+ * MemoryContextId
34
+ * Used for storage of transient identifiers for
35
+ * pg_get_backend_memory_contexts.
36
+ */
37
+ typedef struct MemoryContextId
38
+ {
39
+ MemoryContext context ;
40
+ int context_id ;
41
+ } MemoryContextId ;
42
+
43
+ /*
44
+ * get_memory_context_name_and_ident
45
+ * Populate *name and *ident from the name and ident from 'context'.
46
+ */
47
+ static void
48
+ get_memory_context_name_and_ident (MemoryContext context , const char * * const name ,
49
+ const char * * const ident )
50
+ {
51
+ * name = context -> name ;
52
+ * ident = context -> ident ;
53
+
54
+ /*
55
+ * To be consistent with logging output, we label dynahash contexts with
56
+ * just the hash table name as with MemoryContextStatsPrint().
57
+ */
58
+ if (ident && strcmp (* name , "dynahash" ) == 0 )
59
+ {
60
+ * name = * ident ;
61
+ * ident = NULL ;
62
+ }
63
+ }
64
+
65
+ /*
66
+ * int_list_to_array
67
+ * Convert an IntList to an array of INT4OIDs.
68
+ */
69
+ static Datum
70
+ int_list_to_array (const List * list )
71
+ {
72
+ Datum * datum_array ;
73
+ int length ;
74
+ ArrayType * result_array ;
75
+
76
+ length = list_length (list );
77
+ datum_array = (Datum * ) palloc (length * sizeof (Datum ));
78
+
79
+ foreach_int (i , list )
80
+ datum_array [foreach_current_index (i )] = Int32GetDatum (i );
81
+
82
+ result_array = construct_array_builtin (datum_array , length , INT4OID );
83
+
84
+ return PointerGetDatum (result_array );
85
+ }
86
+
30
87
/*
31
88
* PutMemoryContextsStatsTupleStore
32
- * One recursion level for pg_get_backend_memory_contexts .
89
+ * Add details for the given MemoryContext to 'tupstore' .
33
90
*/
34
91
static void
35
92
PutMemoryContextsStatsTupleStore (Tuplestorestate * tupstore ,
36
93
TupleDesc tupdesc , MemoryContext context ,
37
- const char * parent , int level )
94
+ HTAB * context_id_lookup )
38
95
{
39
- #define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 10
96
+ #define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 11
40
97
41
98
Datum values [PG_GET_BACKEND_MEMORY_CONTEXTS_COLS ];
42
99
bool nulls [PG_GET_BACKEND_MEMORY_CONTEXTS_COLS ];
43
100
MemoryContextCounters stat ;
44
- MemoryContext child ;
101
+ List * path = NIL ;
45
102
const char * name ;
46
103
const char * ident ;
47
104
const char * type ;
48
105
49
106
Assert (MemoryContextIsValid (context ));
50
107
51
- name = context -> name ;
52
- ident = context -> ident ;
53
-
54
108
/*
55
- * To be consistent with logging output, we label dynahash contexts with
56
- * just the hash table name as with MemoryContextStatsPrint() .
109
+ * Figure out the transient context_id of this context and each of its
110
+ * ancestors .
57
111
*/
58
- if ( ident && strcmp ( name , "dynahash" ) == 0 )
112
+ for ( MemoryContext cur = context ; cur != NULL ; cur = cur -> parent )
59
113
{
60
- name = ident ;
61
- ident = NULL ;
114
+ MemoryContextId * entry ;
115
+ bool found ;
116
+
117
+ entry = hash_search (context_id_lookup , & cur , HASH_FIND , & found );
118
+
119
+ if (!found )
120
+ elog (ERROR , "hash table corrupted" );
121
+ path = lcons_int (entry -> context_id , path );
62
122
}
63
123
64
124
/* Examine the context itself */
65
125
memset (& stat , 0 , sizeof (stat ));
66
- (* context -> methods -> stats ) (context , NULL , ( void * ) & level , & stat , true);
126
+ (* context -> methods -> stats ) (context , NULL , NULL , & stat , true);
67
127
68
128
memset (values , 0 , sizeof (values ));
69
129
memset (nulls , 0 , sizeof (nulls ));
70
130
131
+ get_memory_context_name_and_ident (context , & name , & ident );
132
+
71
133
if (name )
72
134
values [0 ] = CStringGetTextDatum (name );
73
135
else
@@ -92,8 +154,15 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
92
154
else
93
155
nulls [1 ] = true;
94
156
95
- if (parent )
96
- values [2 ] = CStringGetTextDatum (parent );
157
+ if (context -> parent )
158
+ {
159
+ const char * parent_name ,
160
+ * parent_ident ;
161
+
162
+ get_memory_context_name_and_ident (context -> parent , & parent_name ,
163
+ & parent_ident );
164
+ values [2 ] = CStringGetTextDatum (parent_name );
165
+ }
97
166
else
98
167
nulls [2 ] = true;
99
168
@@ -117,19 +186,16 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
117
186
}
118
187
119
188
values [3 ] = CStringGetTextDatum (type );
120
- values [4 ] = Int32GetDatum (level );
121
- values [5 ] = Int64GetDatum ( stat . totalspace );
122
- values [6 ] = Int64GetDatum (stat .nblocks );
123
- values [7 ] = Int64GetDatum (stat .freespace );
124
- values [8 ] = Int64GetDatum (stat .freechunks );
125
- values [9 ] = Int64GetDatum (stat .totalspace - stat . freespace );
126
- tuplestore_putvalues ( tupstore , tupdesc , values , nulls );
189
+ values [4 ] = Int32GetDatum (list_length ( path )); /* level */
190
+ values [5 ] = int_list_to_array ( path );
191
+ values [6 ] = Int64GetDatum (stat .totalspace );
192
+ values [7 ] = Int64GetDatum (stat .nblocks );
193
+ values [8 ] = Int64GetDatum (stat .freespace );
194
+ values [9 ] = Int64GetDatum (stat .freechunks );
195
+ values [ 10 ] = Int64GetDatum ( stat . totalspace - stat . freespace );
127
196
128
- for (child = context -> firstchild ; child != NULL ; child = child -> nextchild )
129
- {
130
- PutMemoryContextsStatsTupleStore (tupstore , tupdesc ,
131
- child , name , level + 1 );
132
- }
197
+ tuplestore_putvalues (tupstore , tupdesc , values , nulls );
198
+ list_free (path );
133
199
}
134
200
135
201
/*
@@ -140,10 +206,66 @@ Datum
140
206
pg_get_backend_memory_contexts (PG_FUNCTION_ARGS )
141
207
{
142
208
ReturnSetInfo * rsinfo = (ReturnSetInfo * ) fcinfo -> resultinfo ;
209
+ int context_id ;
210
+ List * contexts ;
211
+ HASHCTL ctl ;
212
+ HTAB * context_id_lookup ;
213
+
214
+ ctl .keysize = sizeof (MemoryContext );
215
+ ctl .entrysize = sizeof (MemoryContextId );
216
+ ctl .hcxt = CurrentMemoryContext ;
217
+
218
+ context_id_lookup = hash_create ("pg_get_backend_memory_contexts" ,
219
+ 256 ,
220
+ & ctl ,
221
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
143
222
144
223
InitMaterializedSRF (fcinfo , 0 );
145
- PutMemoryContextsStatsTupleStore (rsinfo -> setResult , rsinfo -> setDesc ,
146
- TopMemoryContext , NULL , 0 );
224
+
225
+ /*
226
+ * Here we use a non-recursive algorithm to visit all MemoryContexts
227
+ * starting with TopMemoryContext. The reason we avoid using a recursive
228
+ * algorithm is because we want to assign the context_id breadth-first.
229
+ * I.e. all contexts at level 1 are assigned IDs before contexts at level
230
+ * 2. Because contexts closer to TopMemoryContext are less likely to
231
+ * change, this makes the assigned context_id more stable. Otherwise, if
232
+ * the first child of TopMemoryContext obtained an additional grandchild,
233
+ * the context_id for the second child of TopMemoryContext would change.
234
+ */
235
+ contexts = list_make1 (TopMemoryContext );
236
+
237
+ /* TopMemoryContext will always have a context_id of 1 */
238
+ context_id = 1 ;
239
+
240
+ foreach_ptr (MemoryContextData , cur , contexts )
241
+ {
242
+ MemoryContextId * entry ;
243
+ bool found ;
244
+
245
+ /*
246
+ * Record the context_id that we've assigned to each MemoryContext.
247
+ * PutMemoryContextsStatsTupleStore needs this to populate the "path"
248
+ * column with the parent context_ids.
249
+ */
250
+ entry = (MemoryContextId * ) hash_search (context_id_lookup , & cur ,
251
+ HASH_ENTER , & found );
252
+ entry -> context_id = context_id ++ ;
253
+ Assert (!found );
254
+
255
+ PutMemoryContextsStatsTupleStore (rsinfo -> setResult ,
256
+ rsinfo -> setDesc ,
257
+ cur ,
258
+ context_id_lookup );
259
+
260
+ /*
261
+ * Append all children onto the contexts list so they're processed by
262
+ * subsequent iterations.
263
+ */
264
+ for (MemoryContext c = cur -> firstchild ; c != NULL ; c = c -> nextchild )
265
+ contexts = lappend (contexts , c );
266
+ }
267
+
268
+ hash_destroy (context_id_lookup );
147
269
148
270
return (Datum ) 0 ;
149
271
}
0 commit comments