dump_counters.pl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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. # History:
  9. # =======
  10. # 2010-09-01 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 counter_def_t with an initializer is the array
  26. # with the counter definitions (handler pointer, name, flags, callback,
  27. # callback_param, description). Only one counter_def_t array per file is
  28. # supported.
  29. use strict;
  30. use warnings;
  31. use Getopt::Long;
  32. use File::Temp qw(:mktemp);
  33. use File::Basename;
  34. use lib "/home/andrei/perl/modules/share/perl/5.10.1";
  35. use GCC::TranslationUnit;
  36. # text printed if we discover that GCC::TranslationUnit is unpatched
  37. my $patch_required="$0 requires a patched GCC:TranslationUnit, see the " .
  38. "comments at the beginning of the file or try --patch\n";
  39. # gcc name
  40. my $gcc="gcc";
  41. # default defines
  42. 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";
  43. # file with gcc syntax tree
  44. my $file; #"tcp_options.c.001t.tu" ;
  45. my $tmp_file;
  46. my $src_fname;
  47. # type to look for
  48. my $var_type="counter_def_t";
  49. my $tu;
  50. my $node;
  51. my $i;
  52. my @cnt_defs; # filled with counter definitions (counter_def_t)
  53. my ($cnt_grp_name, $def_cnt_name, $cnt_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_varline="VARLINE2";
  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. #print "Usage: $0 --file fname [--src src_fname] [--txt|-t] [--help|-h]\n";
  80. $~ = "USAGE";
  81. write;
  82. format USAGE =
  83. Usage @* -f filename | --file filename [options...]
  84. $0
  85. Options:
  86. -f filename - use filename for input (see also -T/--tu).
  87. --file filename - same as -f.
  88. -h | -? | --help - this help message.
  89. -D | --dbg | --debug - enable debugging messages.
  90. -d | --defs - defines to be passed on gcc's command line
  91. (e.g. --defs="-DUSE_SCTP -DUSE_TCP").
  92. -g | --grp name
  93. --group name - counter group name used if one cannot be
  94. autodetected.
  95. -G | --force-grp name
  96. --force-group name - force using a counter 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 usefull 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. '"' => '&quot;',
  121. "'" => '&apos;',
  122. '&' => '&amp;',
  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_varline="VARLINE2";
  175. }elsif (defined $opt_docbook){
  176. $output_format_header="DOCBOOK_HEADER";
  177. $output_format_footer="DOCBOOK_FOOTER";
  178. $output_format_varline="DOCBOOK_VARLINE";
  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. @cnt_defs == 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. #printf "tree[$i]: found var %s %s (%s)\n",
  260. # $type_name,
  261. # $node->name->identifier,v
  262. # $node->source;
  263. #print ("keys:", join " ", keys %$node, "\n");
  264. #print ("keys init:", join " ", keys %{$node->initial}, "\n");
  265. if ($node->can('initial') && defined $node->initial) {
  266. my %c1=%{$node->initial};
  267. $cnt_name=$node->name->identifier;
  268. if (defined $c1{val}){
  269. my $c1_el;
  270. die $patch_required if (ref($c1{val}) ne "ARRAY");
  271. # iterate on array elem., level 1( top {} )
  272. # each element is a constructor
  273. for $c1_el (@{$c1{val}}) {
  274. # finally we are a the lower {} initializer
  275. my %c2=%{$c1_el};
  276. my @el=@{$c2{val}};
  277. my ($handle_n, $name_n, $flags_n, $cbk_n,
  278. $cbk_param_n, $desc_n)=@el;
  279. my ($name, $desc);
  280. if ($name_n->isa('GCC::Node::integer_cst')){
  281. printf(" ERROR: integer non-0 name (%d)\n",
  282. $name_n->low) if ($name_n->low!=0);
  283. last SEARCH; # exit
  284. }
  285. $name=expr_op0($name_n)->string;
  286. $desc=expr_op0($desc_n)->string;
  287. push @cnt_defs, [$name, $desc];
  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 "Done.\n") if $dbg;
  302. my ($name, $flags, $min, $max, $desc);
  303. my ($type, $extra_txt, $default);
  304. if (@cnt_defs > 0){
  305. my $l;
  306. my $no=@cnt_defs;
  307. $i=0;
  308. # dump the configuration in txt mode
  309. if (defined $opt_force_grp_name) {
  310. $cnt_grp_name=output_esc($opt_force_grp_name);
  311. }elsif (!defined $cnt_grp_name && defined $opt_grp_name) {
  312. $cnt_grp_name=output_esc($opt_grp_name);
  313. }
  314. $~ = $output_format_header; write;
  315. $~ = $output_format_varline ;
  316. for $l (@cnt_defs){
  317. ($name, $desc)=@{$l};
  318. $extra_txt="";
  319. $i++;
  320. $extra_txt=output_esc($extra_txt);
  321. $desc .= "." if $desc =~ /[^.]$/;
  322. $desc=output_esc($desc);
  323. $name=output_esc($name);
  324. # generate txt description
  325. write;
  326. }
  327. $~ = $output_format_footer; write;
  328. }else{
  329. die "no counter variables found in $file\n";
  330. }
  331. sub valid_grp_name
  332. {
  333. my $name=shift;
  334. return defined $name && $name ne "";
  335. }
  336. format HEADER =
  337. Counters@*
  338. (valid_grp_name $cnt_grp_name) ? " for " . $cnt_grp_name : ""
  339. =======================@*
  340. "=" x length((valid_grp_name $cnt_grp_name)?" for " . $cnt_grp_name : "")
  341. @||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  342. "[ this file is autogenerated, do not edit ]"
  343. .
  344. format FOOTER =
  345. .
  346. format VARLINE =
  347. @>. @<<<<<<<<<<<<<<<<<<< - ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  348. $i, $name, $desc
  349. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  350. $desc
  351. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  352. $extra_txt
  353. .
  354. format VARLINE2 =
  355. @>. @*
  356. $i, (valid_grp_name $cnt_grp_name)?$cnt_grp_name . "." . $name : $name
  357. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  358. $desc
  359. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  360. $extra_txt
  361. .
  362. format DOCBOOK_HEADER =
  363. <?xml version="1.0" encoding="UTF-8"?>
  364. <!-- this file is autogenerated, do not edit! -->
  365. <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
  366. "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
  367. <chapter id="counters@*">
  368. (valid_grp_name $cnt_grp_name) ? "." . $cnt_grp_name : ""
  369. <title> Counters@*</title>
  370. (valid_grp_name $cnt_grp_name) ? " for " . $cnt_grp_name : ""
  371. .
  372. format DOCBOOK_FOOTER =
  373. </chapter>
  374. .
  375. format DOCBOOK_VARLINE =
  376. <section id="@*">
  377. (valid_grp_name $cnt_grp_name)?$cnt_grp_name . "." . $name : $name
  378. <title>@*</title>
  379. (valid_grp_name $cnt_grp_name)?$cnt_grp_name . "." . $name : $name
  380. <para>
  381. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  382. $desc
  383. </para>
  384. <para>
  385. ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  386. $extra_txt
  387. </para>
  388. </section>
  389. .