]>
Commit | Line | Data |
---|---|---|
75b07eca FG |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: chrisrd <chris@onthe.net.au> | |
3 | Date: Sat, 24 Feb 2018 03:50:06 +1100 | |
4 | Subject: [PATCH] Fix free memory calculation on v3.14+ | |
5 | MIME-Version: 1.0 | |
6 | Content-Type: text/plain; charset=UTF-8 | |
7 | Content-Transfer-Encoding: 8bit | |
8 | ||
9 | Provide infrastructure to auto-configure to enum and API changes in the | |
10 | global page stats used for our free memory calculations. | |
11 | ||
12 | arc_free_memory has been broken since an API change in Linux v3.14: | |
13 | ||
14 | 2016-07-28 v4.8 599d0c95 mm, vmscan: move LRU lists to node | |
15 | 2016-07-28 v4.8 75ef7184 mm, vmstat: add infrastructure for per-node | |
16 | vmstats | |
17 | ||
18 | These commits moved some of global_page_state() into | |
19 | global_node_page_state(). The API change was particularly egregious as, | |
20 | instead of breaking the old code, it silently did the wrong thing and we | |
21 | continued using global_page_state() where we should have been using | |
22 | global_node_page_state(), thus indexing into the wrong array via | |
23 | NR_SLAB_RECLAIMABLE et al. | |
24 | ||
25 | There have been further API changes along the way: | |
26 | ||
27 | 2017-07-06 v4.13 385386cf mm: vmstat: move slab statistics from zone to | |
28 | node counters | |
29 | 2017-09-06 v4.14 c41f012a mm: rename global_page_state to | |
30 | global_zone_page_state | |
31 | ||
32 | ...and various (incomplete, as it turns out) attempts to accomodate | |
33 | these changes in ZoL: | |
34 | ||
35 | 2017-08-24 2209e409 Linux 4.8+ compatibility fix for vm stats | |
36 | 2017-09-16 787acae0 Linux 3.14 compat: IO acct, global_page_state, etc | |
37 | 2017-09-19 661907e6 Linux 4.14 compat: IO acct, global_page_state, etc | |
38 | ||
39 | The config infrastructure provided here resolves these issues going back | |
40 | to the original API change in v3.14 and is robust against further Linux | |
41 | changes in this area. | |
42 | ||
43 | Reviewed-by: Giuseppe Di Natale <dinatale2@llnl.gov> | |
44 | Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> | |
45 | Reviewed-by: George Melikov <mail@gmelikov.ru> | |
46 | Signed-off-by: Chris Dunlop <chris@onthe.net.au> | |
47 | Closes #7170 | |
48 | (cherry picked from commit 338523dd6ec641cc4d552c3f67e1becfb9e22b0a) | |
49 | Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> | |
50 | --- | |
51 | include/linux/Makefile.am | 3 +- | |
52 | scripts/Makefile.am | 2 + | |
53 | include/linux/page_compat.h | 78 ++++++++++++++++++++++++++ | |
54 | module/zfs/arc.c | 23 ++------ | |
55 | config/kernel-global_page_state.m4 | 109 +++++++++++++++++++++++++++++++++++++ | |
56 | config/kernel-vm_node_stat.m4 | 22 -------- | |
57 | config/kernel.m4 | 2 +- | |
58 | scripts/enum-extract.pl | 58 ++++++++++++++++++++ | |
59 | 8 files changed, 256 insertions(+), 41 deletions(-) | |
60 | create mode 100644 include/linux/page_compat.h | |
61 | create mode 100644 config/kernel-global_page_state.m4 | |
62 | delete mode 100644 config/kernel-vm_node_stat.m4 | |
63 | create mode 100755 scripts/enum-extract.pl | |
64 | ||
65 | diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am | |
66 | index 9bb0b3493..89c2689f6 100644 | |
67 | --- a/include/linux/Makefile.am | |
68 | +++ b/include/linux/Makefile.am | |
69 | @@ -9,7 +9,8 @@ KERNEL_H = \ | |
70 | $(top_srcdir)/include/linux/kmap_compat.h \ | |
71 | $(top_srcdir)/include/linux/simd_x86.h \ | |
72 | $(top_srcdir)/include/linux/simd_aarch64.h \ | |
73 | - $(top_srcdir)/include/linux/mod_compat.h | |
74 | + $(top_srcdir)/include/linux/mod_compat.h \ | |
75 | + $(top_srcdir)/include/linux/page_compat.h | |
76 | ||
77 | USER_H = | |
78 | ||
79 | diff --git a/scripts/Makefile.am b/scripts/Makefile.am | |
80 | index 74b8b31a5..5a8abd135 100644 | |
81 | --- a/scripts/Makefile.am | |
82 | +++ b/scripts/Makefile.am | |
83 | @@ -5,6 +5,7 @@ EXTRA_DIST = dkms.mkconf dkms.postbuild kmodtool zfs2zol-patch.sed cstyle.pl | |
84 | pkgdatadir = $(datadir)/@PACKAGE@ | |
85 | dist_pkgdata_SCRIPTS = \ | |
86 | $(top_builddir)/scripts/common.sh \ | |
87 | + $(top_srcdir)/scripts/enum-extract.pl \ | |
88 | $(top_srcdir)/scripts/zimport.sh \ | |
89 | $(top_srcdir)/scripts/zfs.sh \ | |
90 | $(top_srcdir)/scripts/zfs-tests.sh \ | |
91 | @@ -15,3 +16,4 @@ dist_pkgdata_SCRIPTS = \ | |
92 | $(top_srcdir)/scripts/zpios-survey.sh \ | |
93 | $(top_srcdir)/scripts/smb.sh \ | |
94 | $(top_srcdir)/scripts/zfs-helpers.sh | |
95 | + | |
96 | diff --git a/include/linux/page_compat.h b/include/linux/page_compat.h | |
97 | new file mode 100644 | |
98 | index 000000000..95acb7d53 | |
99 | --- /dev/null | |
100 | +++ b/include/linux/page_compat.h | |
101 | @@ -0,0 +1,78 @@ | |
102 | +#ifndef _ZFS_PAGE_COMPAT_H | |
103 | +#define _ZFS_PAGE_COMPAT_H | |
104 | + | |
105 | +/* | |
106 | + * We have various enum members moving between two separate enum types, | |
107 | + * and accessed by different functions at various times. Centralise the | |
108 | + * insanity. | |
109 | + * | |
110 | + * < v4.8: all enums in zone_stat_item, via global_page_state() | |
111 | + * v4.8: some enums moved to node_stat_item, global_node_page_state() introduced | |
112 | + * v4.13: some enums moved from zone_stat_item to node_state_item | |
113 | + * v4.14: global_page_state() rename to global_zone_page_state() | |
114 | + * | |
115 | + * The defines used here are created by config/kernel-global_page_state.m4 | |
116 | + */ | |
117 | + | |
118 | +/* | |
119 | + * Create our own accessor functions to follow the Linux API changes | |
120 | + */ | |
121 | +#if defined(ZFS_GLOBAL_ZONE_PAGE_STATE) | |
122 | + | |
123 | +/* global_zone_page_state() introduced */ | |
124 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_FILE_PAGES) | |
125 | +#define nr_file_pages() global_node_page_state(NR_FILE_PAGES) | |
126 | +#else | |
127 | +#define nr_file_pages() global_zone_page_state(NR_FILE_PAGES) | |
128 | +#endif | |
129 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON) | |
130 | +#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON) | |
131 | +#else | |
132 | +#define nr_inactive_anon_pages() global_zone_page_state(NR_INACTIVE_ANON) | |
133 | +#endif | |
134 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE) | |
135 | +#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE) | |
136 | +#else | |
137 | +#define nr_inactive_file_pages() global_zone_page_state(NR_INACTIVE_FILE) | |
138 | +#endif | |
139 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE) | |
140 | +#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE) | |
141 | +#else | |
142 | +#define nr_slab_reclaimable_pages() global_zone_page_state(NR_SLAB_RECLAIMABLE) | |
143 | +#endif | |
144 | + | |
145 | +#elif defined(ZFS_GLOBAL_NODE_PAGE_STATE) | |
146 | + | |
147 | +/* global_node_page_state() introduced */ | |
148 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_FILE_PAGES) | |
149 | +#define nr_file_pages() global_node_page_state(NR_FILE_PAGES) | |
150 | +#else | |
151 | +#define nr_file_pages() global_page_state(NR_FILE_PAGES) | |
152 | +#endif | |
153 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON) | |
154 | +#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON) | |
155 | +#else | |
156 | +#define nr_inactive_anon_pages() global_page_state(NR_INACTIVE_ANON) | |
157 | +#endif | |
158 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE) | |
159 | +#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE) | |
160 | +#else | |
161 | +#define nr_inactive_file_pages() global_page_state(NR_INACTIVE_FILE) | |
162 | +#endif | |
163 | +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE) | |
164 | +#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE) | |
165 | +#else | |
166 | +#define nr_slab_reclaimable_pages() global_page_state(NR_SLAB_RECLAIMABLE) | |
167 | +#endif | |
168 | + | |
169 | +#else | |
170 | + | |
171 | +/* global_page_state() only */ | |
172 | +#define nr_file_pages() global_page_state(NR_FILE_PAGES) | |
173 | +#define nr_inactive_anon_pages() global_page_state(NR_INACTIVE_ANON) | |
174 | +#define nr_inactive_file_pages() global_page_state(NR_INACTIVE_FILE) | |
175 | +#define nr_slab_reclaimable_pages() global_page_state(NR_SLAB_RECLAIMABLE) | |
176 | + | |
177 | +#endif /* ZFS_GLOBAL_ZONE_PAGE_STATE */ | |
178 | + | |
179 | +#endif /* _ZFS_PAGE_COMPAT_H */ | |
180 | diff --git a/module/zfs/arc.c b/module/zfs/arc.c | |
181 | index 9d1d0db1d..236794672 100644 | |
182 | --- a/module/zfs/arc.c | |
183 | +++ b/module/zfs/arc.c | |
184 | @@ -280,6 +280,7 @@ | |
185 | #include <sys/fs/swapnode.h> | |
186 | #include <sys/zpl.h> | |
187 | #include <linux/mm_compat.h> | |
188 | +#include <linux/page_compat.h> | |
189 | #endif | |
190 | #include <sys/callb.h> | |
191 | #include <sys/kstat.h> | |
192 | @@ -4016,17 +4017,11 @@ arc_free_memory(void) | |
193 | si_meminfo(&si); | |
194 | return (ptob(si.freeram - si.freehigh)); | |
195 | #else | |
196 | -#ifdef ZFS_GLOBAL_NODE_PAGE_STATE | |
197 | return (ptob(nr_free_pages() + | |
198 | - global_node_page_state(NR_INACTIVE_FILE) + | |
199 | - global_node_page_state(NR_INACTIVE_ANON) + | |
200 | - global_node_page_state(NR_SLAB_RECLAIMABLE))); | |
201 | -#else | |
202 | - return (ptob(nr_free_pages() + | |
203 | - global_page_state(NR_INACTIVE_FILE) + | |
204 | - global_page_state(NR_INACTIVE_ANON) + | |
205 | - global_page_state(NR_SLAB_RECLAIMABLE))); | |
206 | -#endif /* ZFS_GLOBAL_NODE_PAGE_STATE */ | |
207 | + nr_inactive_file_pages() + | |
208 | + nr_inactive_anon_pages() + | |
209 | + nr_slab_reclaimable_pages())); | |
210 | + | |
211 | #endif /* CONFIG_HIGHMEM */ | |
212 | #else | |
213 | return (spa_get_random(arc_all_memory() * 20 / 100)); | |
214 | @@ -4437,13 +4432,7 @@ arc_evictable_memory(void) | |
215 | * Scale reported evictable memory in proportion to page cache, cap | |
216 | * at specified min/max. | |
217 | */ | |
218 | -#ifdef ZFS_GLOBAL_NODE_PAGE_STATE | |
219 | - uint64_t min = (ptob(global_node_page_state(NR_FILE_PAGES)) / 100) * | |
220 | - zfs_arc_pc_percent; | |
221 | -#else | |
222 | - uint64_t min = (ptob(global_page_state(NR_FILE_PAGES)) / 100) * | |
223 | - zfs_arc_pc_percent; | |
224 | -#endif | |
225 | + uint64_t min = (ptob(nr_file_pages()) / 100) * zfs_arc_pc_percent; | |
226 | min = MAX(arc_c_min, MIN(arc_c_max, min)); | |
227 | ||
228 | if (arc_dirty >= min) | |
229 | diff --git a/config/kernel-global_page_state.m4 b/config/kernel-global_page_state.m4 | |
230 | new file mode 100644 | |
231 | index 000000000..f4a40011f | |
232 | --- /dev/null | |
233 | +++ b/config/kernel-global_page_state.m4 | |
234 | @@ -0,0 +1,109 @@ | |
235 | +dnl # | |
236 | +dnl # 4.8 API change | |
237 | +dnl # | |
238 | +dnl # 75ef71840539 mm, vmstat: add infrastructure for per-node vmstats | |
239 | +dnl # 599d0c954f91 mm, vmscan: move LRU lists to node | |
240 | +dnl # | |
241 | +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_NODE_PAGE_STATE], [ | |
242 | + AC_MSG_CHECKING([whether global_node_page_state() exists]) | |
243 | + ZFS_LINUX_TRY_COMPILE([ | |
244 | + #include <linux/mm.h> | |
245 | + #include <linux/vmstat.h> | |
246 | + ],[ | |
247 | + (void) global_node_page_state(0); | |
248 | + ],[ | |
249 | + AC_MSG_RESULT(yes) | |
250 | + AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1, [global_node_page_state() exists]) | |
251 | + ],[ | |
252 | + AC_MSG_RESULT(no) | |
253 | + ]) | |
254 | +]) | |
255 | + | |
256 | +dnl # | |
257 | +dnl # 4.14 API change | |
258 | +dnl # | |
259 | +dnl # c41f012ade0b mm: rename global_page_state to global_zone_page_state | |
260 | +dnl # | |
261 | +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE], [ | |
262 | + AC_MSG_CHECKING([whether global_zone_page_state() exists]) | |
263 | + ZFS_LINUX_TRY_COMPILE([ | |
264 | + #include <linux/mm.h> | |
265 | + #include <linux/vmstat.h> | |
266 | + ],[ | |
267 | + (void) global_zone_page_state(0); | |
268 | + ],[ | |
269 | + AC_MSG_RESULT(yes) | |
270 | + AC_DEFINE(ZFS_GLOBAL_ZONE_PAGE_STATE, 1, [global_zone_page_state() exists]) | |
271 | + ],[ | |
272 | + AC_MSG_RESULT(no) | |
273 | + ]) | |
274 | +]) | |
275 | + | |
276 | +dnl # | |
277 | +dnl # Create a define and autoconf variable for an enum member | |
278 | +dnl # | |
279 | +AC_DEFUN([ZFS_AC_KERNEL_ENUM_MEMBER], [ | |
280 | + AC_MSG_CHECKING([whether enum $2 contains $1]) | |
281 | + AS_IF([AC_TRY_COMMAND("${srcdir}/scripts/enum-extract.pl" "$2" "$3" | egrep -qx $1)],[ | |
282 | + AC_MSG_RESULT([yes]) | |
283 | + AC_DEFINE(m4_join([_], [ZFS_ENUM], m4_toupper($2), $1), 1, [enum $2 contains $1]) | |
284 | + m4_join([_], [ZFS_ENUM], m4_toupper($2), $1)=1 | |
285 | + ],[ | |
286 | + AC_MSG_RESULT([no]) | |
287 | + ]) | |
288 | +]) | |
289 | + | |
290 | +dnl # | |
291 | +dnl # Sanity check helpers | |
292 | +dnl # | |
293 | +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR],[ | |
294 | + AC_MSG_RESULT(no) | |
295 | + AC_MSG_RESULT([$1 in either node_stat_item or zone_stat_item: $2]) | |
296 | + AC_MSG_RESULT([configure needs updating, see: config/kernel-global_page_state.m4]) | |
297 | + AC_MSG_FAILURE([SHUT 'ER DOWN CLANCY, SHE'S PUMPIN' MUD!]) | |
298 | +]) | |
299 | + | |
300 | +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK], [ | |
301 | + enum_check_a="m4_join([_], [$ZFS_ENUM_NODE_STAT_ITEM], $1)" | |
302 | + enum_check_b="m4_join([_], [$ZFS_ENUM_ZONE_STAT_ITEM], $1)" | |
303 | + AS_IF([test -n "$enum_check_a" -a -n "$enum_check_b"],[ | |
304 | + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR([$1], [DUPLICATE]) | |
305 | + ]) | |
306 | + AS_IF([test -z "$enum_check_a" -a -z "$enum_check_b"],[ | |
307 | + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR([$1], [NOT FOUND]) | |
308 | + ]) | |
309 | +]) | |
310 | + | |
311 | +dnl # | |
312 | +dnl # Ensure the config tests are finding one and only one of each enum of interest | |
313 | +dnl # | |
314 | +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY], [ | |
315 | + AC_MSG_CHECKING([global_page_state enums are sane]) | |
316 | + | |
317 | + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_FILE_PAGES]) | |
318 | + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_INACTIVE_ANON]) | |
319 | + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_INACTIVE_FILE]) | |
320 | + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_SLAB_RECLAIMABLE]) | |
321 | + | |
322 | + AC_MSG_RESULT(yes) | |
323 | +]) | |
324 | + | |
325 | +dnl # | |
326 | +dnl # enum members in which we're interested | |
327 | +dnl # | |
328 | +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE], [ | |
329 | + ZFS_AC_KERNEL_GLOBAL_NODE_PAGE_STATE | |
330 | + ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE | |
331 | + | |
332 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_FILE_PAGES], [node_stat_item], [$LINUX/include/linux/mmzone.h]) | |
333 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_ANON], [node_stat_item], [$LINUX/include/linux/mmzone.h]) | |
334 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_FILE], [node_stat_item], [$LINUX/include/linux/mmzone.h]) | |
335 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_SLAB_RECLAIMABLE], [node_stat_item], [$LINUX/include/linux/mmzone.h]) | |
336 | + | |
337 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_FILE_PAGES], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) | |
338 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_ANON], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) | |
339 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_FILE], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) | |
340 | + ZFS_AC_KERNEL_ENUM_MEMBER([NR_SLAB_RECLAIMABLE], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) | |
341 | + | |
342 | + ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY | |
343 | +]) | |
344 | diff --git a/config/kernel-vm_node_stat.m4 b/config/kernel-vm_node_stat.m4 | |
345 | deleted file mode 100644 | |
346 | index 5dcd9d827..000000000 | |
347 | --- a/config/kernel-vm_node_stat.m4 | |
348 | +++ /dev/null | |
349 | @@ -1,22 +0,0 @@ | |
350 | -dnl # | |
351 | -dnl # 4.8 API change | |
352 | -dnl # kernel vm counters change | |
353 | -dnl # | |
354 | -AC_DEFUN([ZFS_AC_KERNEL_VM_NODE_STAT], [ | |
355 | - AC_MSG_CHECKING([whether to use vm_node_stat based fn's]) | |
356 | - ZFS_LINUX_TRY_COMPILE([ | |
357 | - #include <linux/mm.h> | |
358 | - #include <linux/vmstat.h> | |
359 | - ],[ | |
360 | - int a __attribute__ ((unused)) = NR_VM_NODE_STAT_ITEMS; | |
361 | - long x __attribute__ ((unused)) = | |
362 | - atomic_long_read(&vm_node_stat[0]); | |
363 | - (void) global_node_page_state(0); | |
364 | - ],[ | |
365 | - AC_MSG_RESULT(yes) | |
366 | - AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1, | |
367 | - [using global_node_page_state()]) | |
368 | - ],[ | |
369 | - AC_MSG_RESULT(no) | |
370 | - ]) | |
371 | -]) | |
372 | diff --git a/config/kernel.m4 b/config/kernel.m4 | |
373 | index 7bb86a96e..3e499e447 100644 | |
374 | --- a/config/kernel.m4 | |
375 | +++ b/config/kernel.m4 | |
376 | @@ -123,7 +123,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [ | |
377 | ZFS_AC_KERNEL_RENAME_WANTS_FLAGS | |
378 | ZFS_AC_KERNEL_HAVE_GENERIC_SETXATTR | |
379 | ZFS_AC_KERNEL_CURRENT_TIME | |
380 | - ZFS_AC_KERNEL_VM_NODE_STAT | |
381 | + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE | |
382 | ZFS_AC_KERNEL_ACL_HAS_REFCOUNT | |
383 | ||
384 | AS_IF([test "$LINUX_OBJ" != "$LINUX"], [ | |
385 | diff --git a/scripts/enum-extract.pl b/scripts/enum-extract.pl | |
386 | new file mode 100755 | |
387 | index 000000000..5112cc807 | |
388 | --- /dev/null | |
389 | +++ b/scripts/enum-extract.pl | |
390 | @@ -0,0 +1,58 @@ | |
391 | +#!/usr/bin/perl -w | |
392 | + | |
393 | +my $usage = <<EOT; | |
394 | +usage: config-enum enum [file ...] | |
395 | + | |
396 | +Returns the elements from an enum declaration. | |
397 | + | |
398 | +"Best effort": we're not building an entire C interpreter here! | |
399 | +EOT | |
400 | + | |
401 | +use warnings; | |
402 | +use strict; | |
403 | +use Getopt::Std; | |
404 | + | |
405 | +my %opts; | |
406 | + | |
407 | +if (!getopts("", \%opts) || @ARGV < 1) { | |
408 | + print $usage; | |
409 | + exit 2; | |
410 | +} | |
411 | + | |
412 | +my $enum = shift; | |
413 | + | |
414 | +my $in_enum = 0; | |
415 | + | |
416 | +while (<>) { | |
417 | + # comments | |
418 | + s/\/\*.*\*\///; | |
419 | + if (m/\/\*/) { | |
420 | + while ($_ .= <>) { | |
421 | + last if s/\/\*.*\*\///s; | |
422 | + } | |
423 | + } | |
424 | + | |
425 | + # preprocessor stuff | |
426 | + next if /^#/; | |
427 | + | |
428 | + # find our enum | |
429 | + $in_enum = 1 if s/^\s*enum\s+${enum}(?:\s|$)//; | |
430 | + next unless $in_enum; | |
431 | + | |
432 | + # remove explicit values | |
433 | + s/\s*=[^,]+,/,/g; | |
434 | + | |
435 | + # extract each identifier | |
436 | + while (m/\b([a-z_][a-z0-9_]*)\b/ig) { | |
437 | + print $1, "\n"; | |
438 | + } | |
439 | + | |
440 | + # | |
441 | + # don't exit: there may be multiple versions of the same enum, e.g. | |
442 | + # inside different #ifdef blocks. Let's explicitly return all of | |
443 | + # them and let external tooling deal with it. | |
444 | + # | |
445 | + $in_enum = 0 if m/}\s*;/; | |
446 | +} | |
447 | + | |
448 | +exit 0; | |
449 | -- | |
450 | 2.14.2 | |
451 |