dump_rpcs.pl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. #!/usr/bin/perl
  2. #
  3. # Generate docs from ser/sip-router RPCs descriptions
  4. # (run on files generated by gcc -fdump-translation-unit -c file.c,
  5. # try -h for help)
  6. # E.g.: dump_rpcs.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 rpc_export_t with an initializer is the array
  22. # with the rpc definitions (name, doc, flags a.s.o.). Only
  23. # one rpc_export_t array per file is supported.
  24. # - all the documentation arrays referenced in the rpc export array are
  25. # defined and initialized in the same file.
  26. #
  27. # Output notes:
  28. # - doc strings are not printed if they cannot be found
  29. use strict;
  30. use warnings;
  31. use Getopt::Long;
  32. use File::Temp qw(:mktemp);
  33. use File::Basename;
  34. use GCC::TranslationUnit;
  35. # text printed if we discover that GCC::TranslationUnit is unpatched
  36. my $patch_required="$0 requires a patched GCC:TranslationUnit, see the " .
  37. "comments at the beginning of the file or try --patch\n";
  38. # gcc name
  39. my $gcc="gcc";
  40. # default defines
  41. 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='\"/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_BLOCKLIST -DUSE_NAPTR -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_WORKAROUND -DUSE_FUTEX -DHAVE_SELECT";
  42. # file with gcc syntax tree
  43. my $file;
  44. my $tmp_file;
  45. my $src_fname;
  46. # type to look for
  47. my $var_type="rpc_export_t";
  48. my $tu;
  49. my $node;
  50. my $i;
  51. my @rpc_exports; # filled with rpc definitions (rpc_export_t)
  52. my %rpc_docs; # hash containing rpc_doc_varname -> doc_string mappings
  53. my ($rpc_grp_name, $rpc_var_name);
  54. my ($opt_help, $opt_txt, $opt_is_tu, $dbg, $opt_grp_name, $opt_patch);
  55. my ($opt_force_grp_name, $opt_docbook);
  56. # default output formats
  57. my $output_format_header="HEADER";
  58. my $output_format_footer="FOOTER";
  59. my $output_format_rpcline="RPCLINE";
  60. sub show_patch
  61. {
  62. my $patch='
  63. --- GCC/TranslationUnit.pm.orig 2009-10-16 17:57:51.275963053 +0200
  64. +++ GCC/TranslationUnit.pm 2009-10-16 20:17:28.128455959 +0200
  65. @@ -251,6 +251,8 @@
  66. $node->{vector}[$key] = $value;
  67. } elsif($key =~ /^op (\d+)$/) {
  68. $node->{operand}[$1] = $value;
  69. + } elsif ($key eq "val") {
  70. + push @{$node->{$key}}, ($value) ;
  71. } else {
  72. $node->{$key} = $value;
  73. }
  74. ';
  75. print $patch;
  76. }
  77. sub help
  78. {
  79. $~ = "USAGE";
  80. write;
  81. format USAGE =
  82. Usage @* -f filename | --file filename [options...]
  83. $0
  84. Options:
  85. -f filename - use filename for input (see also -T/--tu).
  86. --file filename - same as -f.
  87. -h | -? | --help - this help message.
  88. -D | --dbg | --debug - enable debugging messages.
  89. -d | --defs - defines to be passed on gcc's command line
  90. (e.g. --defs="-DUSE_SCTP -DUSE_TCP").
  91. -g | --grp name
  92. --group name - rpc group name used if one cannot be
  93. autodetected (e.g. no default value
  94. initializer present in the file).
  95. -G | --force-grp name
  96. --force-group name - force using a rpc group name, even if one
  97. is autodetected (see also -g).
  98. --gcc gcc_name - run gcc_name instead of gcc.
  99. -t | --txt - text mode output.
  100. --docbook | --xml - docbook output (xml).
  101. -T | --tu - the input file is in raw gcc translation
  102. unit format (as produced by
  103. gcc -fdump-translation-unit -c ). If not
  104. present it's assumed that the file contains
  105. C code.
  106. -s | --src | --source - name of the source file, needed only if
  107. the input file is in "raw" translation
  108. unit format (--tu) and useful to restrict
  109. and speed-up the search.
  110. --patch - show patches needed for the
  111. GCC::TranslationUnit package.
  112. .
  113. }
  114. # escape a string for xml use
  115. # params: string to be escaped
  116. # return: escaped string
  117. sub xml_escape{
  118. my $s=shift;
  119. my %escapes = (
  120. '"' => '"',
  121. "'" => ''',
  122. '&' => '&',
  123. '<' => '&lt;',
  124. '>' => '&gt;'
  125. );
  126. $s=~s/(["'&<>])/$escapes{$1}/g;
  127. return $s;
  128. }
  129. # escape a string according with the output requirements
  130. # params: string to be escaped
  131. # return: escaped string
  132. sub output_esc{
  133. return xml_escape(shift) if defined $opt_docbook;
  134. return shift;
  135. }
  136. # eliminate casts and expressions.
  137. # (always go on the first operand)
  138. # params: node (GCC::Node)
  139. # result: if node is an expression it will walk on operand(0) until first non
  140. # expression element is found
  141. sub expr_op0{
  142. my $n=shift;
  143. while(($n->isa('GCC::Node::Expression') || $n->isa('GCC::Node::Unary')) &&
  144. defined $n->operand(0)) {
  145. $n=$n->operand(0);
  146. }
  147. return $n;
  148. }
  149. # read command line args
  150. if ($#ARGV < 0 || ! GetOptions( 'help|h|?' => \$opt_help,
  151. 'file|f=s' => \$file,
  152. 'txt|t' => \$opt_txt,
  153. 'docbook|xml' => \$opt_docbook,
  154. 'tu|T' => \$opt_is_tu,
  155. 'source|src|s=s' => \$src_fname,
  156. 'defs|d=s'=>\$c_defs,
  157. 'group|grp|g=s'=>\$opt_grp_name,
  158. 'force-group|force-grp|G=s' =>
  159. \$opt_force_grp_name,
  160. 'dbg|debug|D'=>\$dbg,
  161. 'gcc=s' => \$gcc,
  162. 'patch' => \$opt_patch) ||
  163. defined $opt_help) {
  164. do { show_patch(); exit 0; } if (defined $opt_patch);
  165. select(STDERR) if ! defined $opt_help;
  166. help();
  167. exit((defined $opt_help)?0:1);
  168. }
  169. do { show_patch(); exit 0; } if (defined $opt_patch);
  170. do { select(STDERR); help(); exit 1 } if (!defined $file);
  171. if (defined $opt_txt){
  172. $output_format_header="HEADER";
  173. $output_format_footer="FOOTER";
  174. $output_format_rpcline="RPCLINE";
  175. }elsif (defined $opt_docbook){
  176. $output_format_header="DOCBOOK_HEADER";
  177. $output_format_footer="DOCBOOK_FOOTER";
  178. $output_format_rpcline="DOCBOOK_RPCLINE";
  179. }
  180. if (! defined $opt_is_tu){
  181. # file is not a gcc translation-unit dump
  182. # => we have to create one
  183. $src_fname=basename($file);
  184. $tmp_file = "/tmp/" . mktemp ("dump_translation_unit_XXXXXX");
  185. # Note: gcc < 4.5 will produce the translation unit dump in a file in
  186. # the current directory. gcc 4.5 will write it in the same directory as
  187. # the output file.
  188. system("$gcc -fdump-translation-unit $c_defs -c $file -o $tmp_file") == 0
  189. or die "$gcc -fdump-translation-unit $c_defs -c $file -o $tmp_file" .
  190. " failed to generate a translation unit dump from $file";
  191. if (system("if [ -f \"$src_fname\".001t.tu ]; then \
  192. mv \"$src_fname\".001t.tu $tmp_file; \
  193. else mv /tmp/\"$src_fname\".001t.tu $tmp_file; fi ") != 0) {
  194. unlink($tmp_file, "$tmp_file.o");
  195. die "could not find the gcc translation unit dump file" .
  196. " ($src_fname.001t.tu) neither in the current directory" .
  197. " or /tmp";
  198. };
  199. $tu=GCC::TranslationUnit::Parser->parsefile($tmp_file);
  200. print(STDERR "src name $src_fname\n") if $dbg;
  201. unlink($tmp_file, "$tmp_file.o");
  202. }else{
  203. $tu=GCC::TranslationUnit::Parser->parsefile($file);
  204. }
  205. print(STDERR "Parsing file $file...\n") if $dbg;
  206. #
  207. # function_decl: name, type, srcp (source), chan?, body?, link (e.g. extern)
  208. # parm_decl: name, type, scpe, srcp, chan, argt, size, algn, used
  209. # field_decl: name, type, scpe (scope), srcp, size, algn, bpos (bit pos?)
  210. #
  211. # array_type: size, algn, elts (elements?), domn ?
  212. #
  213. #
  214. # name as string $node->name->identifier
  215. #
  216. # E.g.: static cfg_def_t tcp_cfg_def[]= {...}
  217. # ^^^^^^^^^^^
  218. #
  219. # @7695 var_decl: name: @7705 type: @7706 srcp: tcp_options.c:51
  220. # chan: @7707 init: @7708 size: @7709
  221. # algn: 256 used: 1
  222. #
  223. # @7705 (var name) identifier_node strg: tcp_cfg_def lngt 11
  224. # @7706 (var type) array_type: size:@7709 algn: 32 elts: @2265 domn: @7718
  225. # @7707 (? next ? ) function_decl: ....
  226. # @7708 (initializer) constructor: lngt: 25
  227. # idx : @20 val : @7723 [...]
  228. # @7709 interget_cst: type: @11 low: 5600
  229. #
  230. # @2265 (array type) record_type: name: @2256 unql: @2255 size: @2002
  231. # algn: 32 tag : struct flds: @2263
  232. # @2256 (type) type_decl: name: @2264 type: @2265 srcp: cfg.h:73
  233. # chan: @2266
  234. # @2264 (name) identifier_node: strg: cfg_def_t
  235. print(STDERR "Searching...\n") if $dbg;
  236. $i=0;
  237. # iterate on the entire nodes array (returned by gcc), but skipping node 0
  238. SEARCH: for $node (@{$tu}[1..$#{$tu}]) {
  239. $i++;
  240. while($node) {
  241. if (
  242. @rpc_exports == 0 && # parse it only once
  243. $node->isa('GCC::Node::var_decl') &&
  244. $node->type->isa('GCC::Node::array_type') &&
  245. (! defined $src_fname || $src_fname eq "" ||
  246. $node->source=~"$src_fname")
  247. ){
  248. # found a var decl. that it's an array
  249. # check if it's a valid array type
  250. next if (!( $node->type->can('elements') &&
  251. defined $node->type->elements &&
  252. $node->type->elements->can('name') &&
  253. defined $node->type->elements->name &&
  254. $node->type->elements->name->can('name') &&
  255. defined $node->type->elements->name->name)
  256. );
  257. my $type_name= $node->type->elements->name->name->identifier;
  258. if ($type_name eq $var_type) {
  259. if ($node->can('initial') && defined $node->initial) {
  260. my %c1=%{$node->initial};
  261. $rpc_var_name=$node->name->identifier;
  262. if (defined $c1{val}){
  263. my $c1_el;
  264. die $patch_required if (ref($c1{val}) ne "ARRAY");
  265. # iterate on array elem., level 1( top {} )
  266. # each element is a constructor.
  267. # { name, callback, doc_var, flags }
  268. for $c1_el (@{$c1{val}}) {
  269. # finally we are a the lower {} initializer:
  270. # { name, callback, doc_var, flags }
  271. my %c2=%{$c1_el};
  272. my @el=@{$c2{val}};
  273. my ($name_n, $callback_n, $docvar_n, $flags_n)=@el;
  274. my ($name, $docvar, $flags);
  275. if ($name_n->isa('GCC::Node::integer_cst')){
  276. printf(" ERROR: integer non-0 name (%d)\n",
  277. $name_n->low) if ($name_n->low!=0);
  278. last SEARCH;
  279. }
  280. $name_n=expr_op0($name_n);
  281. $name= $name_n->string;
  282. $flags=$flags_n->low;
  283. # eliminate casts and expressions
  284. # (always go on the first operand)
  285. $docvar_n=expr_op0($docvar_n);
  286. $docvar=$docvar_n->name->identifier;
  287. push @rpc_exports, [$name, $docvar, $flags];
  288. }
  289. }
  290. }
  291. }
  292. }
  293. } continue {
  294. if ($node->can('chain')){
  295. $node = $node->chain;
  296. }else{
  297. last;
  298. }
  299. }
  300. }
  301. print(STDERR "Searching doc vars...\n") if $dbg;
  302. # look for docvars
  303. # re-iterate on the entire nodes array (returned by gcc), but skipping node 0
  304. DOC: for $node (@{$tu}[1..$#{$tu}]) {
  305. while(@rpc_exports>0 && $node) {
  306. if (
  307. $node->isa('GCC::Node::var_decl') &&
  308. $node->type->isa('GCC::Node::array_type') &&
  309. (! defined $src_fname || $src_fname eq "" ||
  310. $node->source=~"$src_fname") &&
  311. # var name is among the one we look for
  312. grep(${$_}[1] eq $node->name->identifier, @rpc_exports) > 0
  313. ){
  314. print(STDERR "found a candidate:", $node->name->identifier, "\n")
  315. if $dbg;
  316. # found a var decl. that it's an array
  317. # check if it's a valid array type
  318. next if (!( $node->type->can('elements') &&
  319. defined $node->type->elements)
  320. );
  321. if ($node->can('initial') && defined $node->initial){
  322. my %c1=%{$node->initial};
  323. my $doc_n = ${$c1{val}}[0];
  324. if (defined $doc_n){
  325. my $doc=expr_op0($doc_n)->string;
  326. $rpc_docs{$node->name->identifier}=$doc;
  327. last DOC if ( @rpc_exports == keys %rpc_docs );
  328. }
  329. }
  330. }
  331. } continue {
  332. if ($node->can('chain')){
  333. $node = $node->chain;
  334. }else{
  335. last;
  336. }
  337. }
  338. }
  339. print(STDERR "Done.\n") if $dbg;
  340. my ($name, $flags, $desc);
  341. my $extra_txt;
  342. if (@rpc_exports > 0){
  343. my $l;
  344. $i=0;
  345. if (@rpc_exports != keys %rpc_docs){
  346. print STDERR "Warning: missing ", @rpc_exports - keys %rpc_docs,
  347. " doc variables definitions\n";
  348. }
  349. # dump the configuration in txt mode
  350. if (defined $opt_force_grp_name) {
  351. $rpc_grp_name=output_esc($opt_force_grp_name);
  352. }elsif (!defined $rpc_grp_name && defined $opt_grp_name) {
  353. $rpc_grp_name=output_esc($opt_grp_name);
  354. }
  355. $~ = $output_format_header; write;
  356. $~ = $output_format_rpcline ;
  357. for $l (@rpc_exports){
  358. ($name, $desc, $flags)=@{$l};
  359. $extra_txt="";
  360. $desc=(defined $rpc_docs{$desc} && $rpc_docs{$desc} ne "")?
  361. output_esc($rpc_docs{$desc}):
  362. output_esc("Documentation missing ($desc).");
  363. $i++;
  364. $extra_txt.=output_esc("Returns an array.") if ($flags & 1 );
  365. $name=output_esc($name);
  366. # generate txt description
  367. write;
  368. }
  369. $~ = $output_format_footer; write;
  370. }else{
  371. die "no rpc exports found in $file\n";
  372. }
  373. sub valid_grp_name
  374. {
  375. my $name=shift;
  376. return defined $name && $name ne "";
  377. }
  378. format HEADER =
  379. RPC Exports@*
  380. (valid_grp_name $rpc_grp_name) ? " for " . $rpc_grp_name : ""
  381. ===========@*
  382. "=" x length((valid_grp_name $rpc_grp_name)?" for " . $rpc_grp_name : "")
  383. @||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  384. "[ this file is autogenerated, do not edit ]"
  385. .
  386. format FOOTER =
  387. .
  388. format RPCLINE =
  389. @>. @*
  390. $i, $name
  391. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  392. $desc
  393. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  394. $extra_txt
  395. .
  396. format DOCBOOK_HEADER =
  397. <?xml version="1.0" encoding="UTF-8"?>
  398. <!-- this file is autogenerated, do not edit! -->
  399. <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
  400. "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
  401. <chapter id="rpc_exports@*">
  402. (valid_grp_name $rpc_grp_name) ? "." . $rpc_grp_name : ""
  403. <title>
  404. RPC Exports@*
  405. (valid_grp_name $rpc_grp_name) ? " for " . $rpc_grp_name : ""
  406. </title>
  407. .
  408. format DOCBOOK_FOOTER =
  409. </chapter>
  410. .
  411. format DOCBOOK_RPCLINE =
  412. <section id="@*"><title>@*</title>
  413. $name, $name
  414. <para>
  415. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  416. $desc
  417. </para>
  418. <para>
  419. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  420. $extra_txt
  421. </para>
  422. </section>
  423. .