dump_counters.pl 14 KB

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