dump_cfg_defs.pl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. #!/usr/bin/perl
  2. #
  3. # Generate docs from ser/sip-router cfg group descriptions
  4. # (run on files generated by gcc -fdump-translation-unit -c file.c,
  5. # try -h for help)
  6. # E.g.: dump_cfg_defs.pl --file cfg_core.c --defs="-DUSE_SCTP ..."
  7. #
  8. # History:
  9. # =======
  10. # 2009-10-16 initial version (Andrei Pelinescu-Onciul <[email protected]>)
  11. #
  12. # Note: uses GCC::TranslationUnit (see cpan) with the following patch:
  13. #@@ -251,6 +251,8 @@
  14. # $node->{vector}[$key] = $value;
  15. # } elsif($key =~ /^op (\d+)$/) {
  16. # $node->{operand}[$1] = $value;
  17. #+ } elsif ($key eq "val") {
  18. #+ push @{$node->{$key}}, ($value) ;
  19. # } else {
  20. # $node->{$key} = $value;
  21. # }
  22. #
  23. #
  24. # Assumptions:
  25. # - the first array of type cfg_def_t with an initializer is the array
  26. # with the config definitions (name, type, description a.s.o.). Only
  27. # one cfg_def array per file is supported.
  28. # - the first variable of type struct cfg_group_(.*) , with an initializer,
  29. # contains the default values. If no group name is specified on the
  30. # command line (--group) the group name is derived from the struct
  31. # name ($1). The default values are optional. Only one such variable is
  32. # supported per file.
  33. # - if the number of the default values is different from the number of
  34. # elements in the config defs array, the default values are discarded.
  35. #
  36. # Output notes:
  37. # - range is not printed if min==max==0.
  38. # - default values are not printed if missing
  39. use strict;
  40. use warnings;
  41. use Getopt::Long;
  42. use File::Temp qw(:mktemp);
  43. use File::Basename;
  44. use lib "/home/andrei/perl/modules/share/perl/5.10.1";
  45. use GCC::TranslationUnit;
  46. # text printed if we discover that GCC::TranslationUnit is unpatched
  47. my $patch_required="$0 requires a patched GCC:TranslationUnit, see the " .
  48. "comments at the beginning of the file or try --patch\n";
  49. # gcc name
  50. my $gcc="gcc";
  51. # default defines
  52. my $c_defs="-D__CPU_i386 -D__OS_linux -DSER_VER=2099099 -DPKG_MALLOC -DSHM_MEM -DSHM_MMAP -DDNS_IP_HACK -DUSE_IPV6 -DUSE_MCAST -DUSE_TCP -DUSE_DNS_CACHE -DUSE_DNS_FAILOVER -DUSE_DST_BLACKLIST -DUSE_NAPTR -DUSE_TLS -DTLS_HOOKS -DFAST_LOCK -DCC_GCC_LIKE_ASM -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H -DHAVE_SCHED_SETSCHEDULER -DHAVE_EPOLL -DUSE_SCTP -DNAME='\"ser\"' -DVERSION='\"2.99.99-pre3\"' -DARCH='\"i386\"' -DOS_QUOTED='\"linux\"' -DSER_MOD_INTERFACE";
  53. # file with gcc syntax tree
  54. my $file; #"tcp_options.c.001t.tu" ;
  55. my $tmp_file;
  56. my $src_fname;
  57. # type to look for
  58. my $var_type="cfg_def_t";
  59. my $tu;
  60. my $node;
  61. my $i;
  62. my @cfg_defs; # filled with config var definition (cfg_def_t)
  63. my @cfg_default; # filled with config var defaults
  64. my ($cfg_grp_name, $def_cfg_name, $cfg_var_name);
  65. my ($opt_help, $opt_txt, $opt_is_tu, $dbg, $opt_grp_name, $opt_patch);
  66. my ($opt_force_grp_name, $opt_docbook);
  67. # default output formats
  68. my $output_format_header="HEADER";
  69. my $output_format_footer="FOOTER";
  70. my $output_format_varline="VARLINE2";
  71. sub show_patch
  72. {
  73. my $patch='
  74. --- GCC/TranslationUnit.pm.orig 2009-10-16 17:57:51.275963053 +0200
  75. +++ GCC/TranslationUnit.pm 2009-10-16 20:17:28.128455959 +0200
  76. @@ -251,6 +251,8 @@
  77. $node->{vector}[$key] = $value;
  78. } elsif($key =~ /^op (\d+)$/) {
  79. $node->{operand}[$1] = $value;
  80. + } elsif ($key eq "val") {
  81. + push @{$node->{$key}}, ($value) ;
  82. } else {
  83. $node->{$key} = $value;
  84. }
  85. ';
  86. print $patch;
  87. }
  88. sub help
  89. {
  90. #print "Usage: $0 --file fname [--src src_fname] [--txt|-t] [--help|-h]\n";
  91. $~ = "USAGE";
  92. write;
  93. format USAGE =
  94. Usage @* -f filename | --file filename [options...]
  95. $0
  96. Options:
  97. -f filename - use filename for input (see also -T/--tu).
  98. --file filename - same as -f.
  99. -h | -? | --help - this help message.
  100. -D | --dbg | --debug - enable debugging messages.
  101. -d | --defs - defines to be passed on gcc's command line
  102. (e.g. --defs="-DUSE_SCTP -DUSE_TCP").
  103. -g | --grp name
  104. --group name - config group name used if one cannot be
  105. autodetected (e.g. no default value
  106. initializer present in the file).
  107. -G | --force-grp name
  108. --force-group name - force using a config group name, even if one
  109. is autodetected (see also -g).
  110. --gcc gcc_name - run gcc_name instead of gcc.
  111. -t | --txt - text mode output.
  112. --docbook | --xml - docbook output (xml).
  113. -T | --tu - the input file is in raw gcc translation
  114. unit format (as produced by
  115. gcc -fdump-translation-unit -c ). If not
  116. present it's assumed that the file contains
  117. C code.
  118. -s | --src | --source - name of the source file, needed only if
  119. the input file is in "raw" translation
  120. unit format (--tu) and usefull to restrict
  121. and speed-up the search.
  122. --patch - show patches needed for the
  123. GCC::TranslationUnit package.
  124. .
  125. }
  126. # escape a string for xml use
  127. # params: string to be escaped
  128. # return: escaped string
  129. sub xml_escape{
  130. my $s=shift;
  131. my %escapes = (
  132. '"' => '&quot;',
  133. "'" => '&apos;',
  134. '&' => '&amp;',
  135. '<' => '&lt;',
  136. '>' => '&gt;'
  137. );
  138. $s=~s/(["'&<>])/$escapes{$1}/g;
  139. return $s;
  140. }
  141. # escape a string according with the output requirements
  142. # params: string to be escaped
  143. # return: escaped string
  144. sub output_esc{
  145. return xml_escape(shift) if defined $opt_docbook;
  146. return shift;
  147. }
  148. # eliminate casts and expressions.
  149. # (always go on the first operand)
  150. # params: node (GCC::Node)
  151. # result: if node is an expression it will walk on operand(0) until first non
  152. # expression element is found
  153. sub expr_op0{
  154. my $n=shift;
  155. while(($n->isa('GCC::Node::Expression') || $n->isa('GCC::Node::Unary')) &&
  156. defined $n->operand(0)) {
  157. $n=$n->operand(0);
  158. }
  159. return $n;
  160. }
  161. # read command line args
  162. if ($#ARGV < 0 || ! GetOptions( 'help|h|?' => \$opt_help,
  163. 'file|f=s' => \$file,
  164. 'txt|t' => \$opt_txt,
  165. 'docbook|xml' => \$opt_docbook,
  166. 'tu|T' => \$opt_is_tu,
  167. 'source|src|s=s' => \$src_fname,
  168. 'defs|d=s'=>\$c_defs,
  169. 'group|grp|g=s'=>\$opt_grp_name,
  170. 'force-group|force-grp|G=s' =>
  171. \$opt_force_grp_name,
  172. 'dbg|debug|D'=>\$dbg,
  173. 'gcc=s' => \$gcc,
  174. 'patch' => \$opt_patch) ||
  175. defined $opt_help) {
  176. do { show_patch(); exit 0; } if (defined $opt_patch);
  177. select(STDERR) if ! defined $opt_help;
  178. help();
  179. exit((defined $opt_help)?0:1);
  180. }
  181. do { show_patch(); exit 0; } if (defined $opt_patch);
  182. do { select(STDERR); help(); exit 1 } if (!defined $file);
  183. if (defined $opt_txt){
  184. $output_format_header="HEADER";
  185. $output_format_footer="FOOTER";
  186. $output_format_varline="VARLINE2";
  187. }elsif (defined $opt_docbook){
  188. $output_format_header="DOCBOOK_HEADER";
  189. $output_format_footer="DOCBOOK_FOOTER";
  190. $output_format_varline="DOCBOOK_VARLINE";
  191. }
  192. if (! defined $opt_is_tu){
  193. # file is not a gcc translation-unit dump
  194. # => we have to create one
  195. $src_fname=basename($file);
  196. $tmp_file = "/tmp/" . mktemp ("dump_translation_unit_XXXXXX");
  197. # Note: gcc < 4.5 will produce the translation unit dump in a file in
  198. # the current directory. gcc 4.5 will write it in the same directory as
  199. # the output file.
  200. system("$gcc -fdump-translation-unit $c_defs -c $file -o $tmp_file") == 0
  201. or die "$gcc -fdump-translation-unit $c_defs -c $file -o $tmp_file" .
  202. " failed to generate a translation unit dump from $file";
  203. if (system("if [ -f \"$src_fname\".001t.tu ]; then \
  204. mv \"$src_fname\".001t.tu $tmp_file; \
  205. else mv /tmp/\"$src_fname\".001t.tu $tmp_file; fi ") != 0) {
  206. unlink($tmp_file, "$tmp_file.o");
  207. die "could not find the gcc translation unit dump file" .
  208. " ($src_fname.001t.tu) neither in the current directory" .
  209. " or /tmp";
  210. };
  211. $tu=GCC::TranslationUnit::Parser->parsefile($tmp_file);
  212. print(STDERR "src name $src_fname\n") if $dbg;
  213. unlink($tmp_file, "$tmp_file.o");
  214. }else{
  215. $tu=GCC::TranslationUnit::Parser->parsefile($file);
  216. }
  217. print(STDERR "Parsing file $file...\n") if $dbg;
  218. #
  219. # function_decl: name, type, srcp (source), chan?, body?, link (e.g. extern)
  220. # parm_decl: name, type, scpe, srcp, chan, argt, size, algn, used
  221. # field_decl: name, type, scpe (scope), srcp, size, algn, bpos (bit pos?)
  222. #
  223. # array_type: size, algn, elts (elements?), domn ?
  224. #
  225. #
  226. # name as string $node->name->identifier
  227. #
  228. # E.g.: static cfg_def_t tcp_cfg_def[]= {...}
  229. # ^^^^^^^^^^^
  230. #
  231. # @7695 var_decl: name: @7705 type: @7706 srcp: tcp_options.c:51
  232. # chan: @7707 init: @7708 size: @7709
  233. # algn: 256 used: 1
  234. #
  235. # @7705 (var name) identifier_node strg: tcp_cfg_def lngt 11
  236. # @7706 (var type) array_type: size:@7709 algn: 32 elts: @2265 domn: @7718
  237. # @7707 (? next ? ) function_decl: ....
  238. # @7708 (initializer) constructor: lngt: 25
  239. # idx : @20 val : @7723 [...]
  240. # @7709 interget_cst: type: @11 low: 5600
  241. #
  242. # @2265 (array type) record_type: name: @2256 unql: @2255 size: @2002
  243. # algn: 32 tag : struct flds: @2263
  244. # @2256 (type) type_decl: name: @2264 type: @2265 srcp: cfg.h:73
  245. # chan: @2266
  246. # @2264 (name) identifier_node: strg: cfg_def_t
  247. print(STDERR "Searching...\n") if $dbg;
  248. $i=0;
  249. # iterate on the entire nodes array (returned by gcc), but skipping node 0
  250. SEARCH: for $node (@{$tu}[1..$#{$tu}]) {
  251. $i++;
  252. while($node) {
  253. if (
  254. @cfg_defs == 0 && # parse it only once
  255. $node->isa('GCC::Node::var_decl') &&
  256. $node->type->isa('GCC::Node::array_type') &&
  257. (! defined $src_fname || $src_fname eq "" ||
  258. $node->source=~"$src_fname")
  259. ){
  260. # found a var decl. that it's an array
  261. # check if it's a valid array type
  262. next if (!( $node->type->can('elements') &&
  263. defined $node->type->elements &&
  264. $node->type->elements->can('name') &&
  265. defined $node->type->elements->name &&
  266. $node->type->elements->name->can('name') &&
  267. defined $node->type->elements->name->name)
  268. );
  269. my $type_name= $node->type->elements->name->name->identifier;
  270. if ($type_name eq $var_type) {
  271. #printf "tree[$i]: found var %s %s (%s)\n",
  272. # $type_name,
  273. # $node->name->identifier,v
  274. # $node->source;
  275. #print ("keys:", join " ", keys %$node, "\n");
  276. #print ("keys init:", join " ", keys %{$node->initial}, "\n");
  277. if ($node->can('initial') && defined $node->initial) {
  278. my %c1=%{$node->initial};
  279. $cfg_var_name=$node->name->identifier;
  280. if (defined $c1{val}){
  281. my $c1_el;
  282. die $patch_required if (ref($c1{val}) ne "ARRAY");
  283. # iterate on array elem., level 1( top {} )
  284. # each element is a constructor
  285. for $c1_el (@{$c1{val}}) {
  286. # finally we are a the lower {} initializer
  287. my %c2=%{$c1_el};
  288. my @el=@{$c2{val}};
  289. my ($name_n, $type_n, $min_n, $max_n, $fixup_n,
  290. $pcbk_n, $desc_n)=@el;
  291. my ($name, $type, $min, $max, $desc);
  292. if ($name_n->isa('GCC::Node::integer_cst')){
  293. printf(" ERROR: integer non-0 name (%d)\n",
  294. $name_n->low) if ($name_n->low!=0);
  295. if (@cfg_default > 0){
  296. last SEARCH; # exit
  297. }else{
  298. next; # have to look for defaults too
  299. }
  300. }
  301. $name=expr_op0($name_n)->string;
  302. $type=$type_n->low;
  303. $min=$min_n->low;
  304. $max=$max_n->low;
  305. $desc=expr_op0($desc_n)->string;
  306. push @cfg_defs, [$name, $type, $min, $max, $desc];
  307. }
  308. }
  309. }
  310. }
  311. }elsif (@cfg_default == 0 && # parse it only once
  312. $node->isa('GCC::Node::var_decl') &&
  313. $node->type->isa('GCC::Node::record_type') &&
  314. (! defined $src_fname || $src_fname eq "" ||
  315. $node->source=~"$src_fname") &&
  316. defined $node->type->name->can('identifier') &&
  317. $node->type->name->identifier=~"cfg_group_([a-z0-9_]+)" &&
  318. $node->can('initial') && defined $node->initial) {
  319. my %c1=%{$node->initial};
  320. if (defined $c1{val}){
  321. my $c1_el;
  322. $cfg_grp_name=$1;
  323. $def_cfg_name=$node->name->identifier;
  324. print(STDERR "found default cfg: $def_cfg_name,",
  325. "grp $cfg_grp_name\n") if $dbg;
  326. die $patch_required if (ref($c1{val}) ne "ARRAY");
  327. # iterate on array elem.,( top {} )
  328. # each element is an integer, expr (string pointer) or
  329. # constructor (str)
  330. for $c1_el (@{$c1{val}}) {
  331. if ($c1_el->isa('GCC::Node::integer_cst')){
  332. push @cfg_default, $c1_el->low;
  333. }elsif ($c1_el->isa('GCC::Node::constructor')){
  334. push @cfg_default, "<unknown:str>";
  335. }else{
  336. push @cfg_default, expr_op0($c1_el)->string;
  337. }
  338. }
  339. last SEARCH if @cfg_defs > 0; # exit
  340. }
  341. }
  342. } continue {
  343. if ($node->can('chain')){
  344. $node = $node->chain;
  345. }else{
  346. last;
  347. }
  348. }
  349. }
  350. print(STDERR "Done.\n") if $dbg;
  351. my ($name, $flags, $min, $max, $desc);
  352. my ($type, $extra_txt, $default);
  353. if (@cfg_defs > 0){
  354. my $l;
  355. my $no=@cfg_default;
  356. $i=0;
  357. if ($no > 0 && @cfg_defs != $no) {
  358. print(STDERR "WARNING: different array lenghts ($def_cfg_name($no) &&",
  359. " $cfg_var_name($(scalar @cfg_defs)))\n");
  360. $no=0;
  361. }
  362. # dump the configuration in txt mode
  363. if (defined $opt_force_grp_name) {
  364. $cfg_grp_name=output_esc($opt_force_grp_name);
  365. }elsif (!defined $cfg_grp_name && defined $opt_grp_name) {
  366. $cfg_grp_name=output_esc($opt_grp_name);
  367. }
  368. $~ = $output_format_header; write;
  369. $~ = $output_format_varline ;
  370. for $l (@cfg_defs){
  371. ($name, $flags, $min, $max, $desc)=@{$l};
  372. $type="";
  373. $extra_txt="";
  374. $default= ($no>0) ? output_esc($cfg_default[$i]) : "";
  375. $i++;
  376. if ($min==0 && $max==0) {
  377. $min=""; $max="";
  378. }
  379. if ($flags & 8) {
  380. $type="integer";
  381. }elsif ($flags & 16) {
  382. $type="string";
  383. }elsif ($flags & 32) {
  384. $type="string"; # str
  385. }else{
  386. my $t = $flags & 7;
  387. $t == 1 && do { $type="integer"; };
  388. $t == 2 && do { $type="string"; };
  389. $t == 3 && do { $type="string"; }; # str
  390. $t == 4 && do { $type=""; }; # pointer
  391. }
  392. $extra_txt.="Read-only." if ($flags & 128 );
  393. $extra_txt=output_esc($extra_txt);
  394. $desc=output_esc($desc . ".");
  395. $name=output_esc($name);
  396. # generate txt description
  397. write;
  398. }
  399. $~ = $output_format_footer; write;
  400. }else{
  401. die "no configuration variables found in $file\n";
  402. }
  403. sub valid_grp_name
  404. {
  405. my $name=shift;
  406. return defined $name && $name ne "";
  407. }
  408. format HEADER =
  409. Configuration Variables@*
  410. (valid_grp_name $cfg_grp_name) ? " for " . $cfg_grp_name : ""
  411. =======================@*
  412. "=" x length((valid_grp_name $cfg_grp_name)?" for " . $cfg_grp_name : "")
  413. @||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  414. "[ this file is autogenerated, do not edit ]"
  415. .
  416. format FOOTER =
  417. .
  418. format VARLINE =
  419. @>. @<<<<<<<<<<<<<<<<<<< - ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  420. $i, $name, $desc
  421. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  422. $desc
  423. ~ Default: @*.
  424. $default
  425. ~ Range: @* - @*.
  426. $min, $max
  427. ~ Type: @*. ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  428. $type, $extra_txt
  429. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  430. $extra_txt
  431. .
  432. format VARLINE2 =
  433. @>. @*
  434. $i, (valid_grp_name $cfg_grp_name)?$cfg_grp_name . "." . $name : $name
  435. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  436. $desc
  437. ~ Default: @*.
  438. $default
  439. ~ Range: @* - @*.
  440. $min, $max
  441. ~ Type: @*. ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  442. $type, $extra_txt
  443. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  444. $extra_txt
  445. .
  446. format DOCBOOK_HEADER =
  447. <?xml version="1.0" encoding="UTF-8"?>
  448. <!-- this file is autogenerated, do not edit! -->
  449. <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
  450. "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
  451. <chapter id="config_vars@*">
  452. (valid_grp_name $cfg_grp_name) ? "." . $cfg_grp_name : ""
  453. <title> Configuration Variables@*</title>
  454. (valid_grp_name $cfg_grp_name) ? " for " . $cfg_grp_name : ""
  455. .
  456. format DOCBOOK_FOOTER =
  457. </chapter>
  458. .
  459. format DOCBOOK_VARLINE =
  460. <section id="@*">
  461. (valid_grp_name $cfg_grp_name)?$cfg_grp_name . "." . $name : $name
  462. <title>@*</title>
  463. (valid_grp_name $cfg_grp_name)?$cfg_grp_name . "." . $name : $name
  464. <para>
  465. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  466. $desc
  467. </para>
  468. ~ <para>Default value: @*.</para>
  469. $default
  470. ~ <para>Range: @* - @*.</para>
  471. $min, $max
  472. ~ <para>Type: @*.</para>
  473. $type
  474. <para>
  475. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  476. $extra_txt
  477. </para>
  478. </section>
  479. .