dump_cfg_defs.pl 17 KB

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