get_abi.pl 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103
  1. #!/usr/bin/env perl
  2. # SPDX-License-Identifier: GPL-2.0
  3. BEGIN { $Pod::Usage::Formatter = 'Pod::Text::Termcap'; }
  4. use strict;
  5. use warnings;
  6. use utf8;
  7. use Pod::Usage qw(pod2usage);
  8. use Getopt::Long;
  9. use File::Find;
  10. use IO::Handle;
  11. use Fcntl ':mode';
  12. use Cwd 'abs_path';
  13. use Data::Dumper;
  14. my $help = 0;
  15. my $hint = 0;
  16. my $man = 0;
  17. my $debug = 0;
  18. my $enable_lineno = 0;
  19. my $show_warnings = 1;
  20. my $prefix="Documentation/ABI";
  21. my $sysfs_prefix="/sys";
  22. my $search_string;
  23. # Debug options
  24. my $dbg_what_parsing = 1;
  25. my $dbg_what_open = 2;
  26. my $dbg_dump_abi_structs = 4;
  27. my $dbg_undefined = 8;
  28. $Data::Dumper::Indent = 1;
  29. $Data::Dumper::Terse = 1;
  30. #
  31. # If true, assumes that the description is formatted with ReST
  32. #
  33. my $description_is_rst = 1;
  34. GetOptions(
  35. "debug=i" => \$debug,
  36. "enable-lineno" => \$enable_lineno,
  37. "rst-source!" => \$description_is_rst,
  38. "dir=s" => \$prefix,
  39. 'help|?' => \$help,
  40. "show-hints" => \$hint,
  41. "search-string=s" => \$search_string,
  42. man => \$man
  43. ) or pod2usage(2);
  44. pod2usage(1) if $help;
  45. pod2usage(-exitstatus => 0, -noperldoc, -verbose => 2) if $man;
  46. pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2);
  47. my ($cmd, $arg) = @ARGV;
  48. pod2usage(2) if ($cmd ne "search" && $cmd ne "rest" && $cmd ne "validate" && $cmd ne "undefined");
  49. pod2usage(2) if ($cmd eq "search" && !$arg);
  50. require Data::Dumper if ($debug & $dbg_dump_abi_structs);
  51. my %data;
  52. my %symbols;
  53. #
  54. # Displays an error message, printing file name and line
  55. #
  56. sub parse_error($$$$) {
  57. my ($file, $ln, $msg, $data) = @_;
  58. return if (!$show_warnings);
  59. $data =~ s/\s+$/\n/;
  60. print STDERR "Warning: file $file#$ln:\n\t$msg";
  61. if ($data ne "") {
  62. print STDERR ". Line\n\t\t$data";
  63. } else {
  64. print STDERR "\n";
  65. }
  66. }
  67. #
  68. # Parse an ABI file, storing its contents at %data
  69. #
  70. sub parse_abi {
  71. my $file = $File::Find::name;
  72. my $mode = (stat($file))[2];
  73. return if ($mode & S_IFDIR);
  74. return if ($file =~ m,/README,);
  75. return if ($file =~ m,/\.,);
  76. return if ($file =~ m,\.(rej|org|orig|bak)$,);
  77. my $name = $file;
  78. $name =~ s,.*/,,;
  79. my $fn = $file;
  80. $fn =~ s,.*Documentation/ABI/,,;
  81. my $nametag = "File $fn";
  82. $data{$nametag}->{what} = "File $name";
  83. $data{$nametag}->{type} = "File";
  84. $data{$nametag}->{file} = $name;
  85. $data{$nametag}->{filepath} = $file;
  86. $data{$nametag}->{is_file} = 1;
  87. $data{$nametag}->{line_no} = 1;
  88. my $type = $file;
  89. $type =~ s,.*/(.*)/.*,$1,;
  90. my $what;
  91. my $new_what;
  92. my $tag = "";
  93. my $ln;
  94. my $xrefs;
  95. my $space;
  96. my @labels;
  97. my $label = "";
  98. print STDERR "Opening $file\n" if ($debug & $dbg_what_open);
  99. open IN, $file;
  100. while(<IN>) {
  101. $ln++;
  102. if (m/^(\S+)(:\s*)(.*)/i) {
  103. my $new_tag = lc($1);
  104. my $sep = $2;
  105. my $content = $3;
  106. if (!($new_tag =~ m/(what|where|date|kernelversion|contact|description|users)/)) {
  107. if ($tag eq "description") {
  108. # New "tag" is actually part of
  109. # description. Don't consider it a tag
  110. $new_tag = "";
  111. } elsif ($tag ne "") {
  112. parse_error($file, $ln, "tag '$tag' is invalid", $_);
  113. }
  114. }
  115. # Invalid, but it is a common mistake
  116. if ($new_tag eq "where") {
  117. parse_error($file, $ln, "tag 'Where' is invalid. Should be 'What:' instead", "");
  118. $new_tag = "what";
  119. }
  120. if ($new_tag =~ m/what/) {
  121. $space = "";
  122. $content =~ s/[,.;]$//;
  123. push @{$symbols{$content}->{file}}, " $file:" . ($ln - 1);
  124. if ($tag =~ m/what/) {
  125. $what .= "\xac" . $content;
  126. } else {
  127. if ($what) {
  128. parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description});
  129. foreach my $w(split /\xac/, $what) {
  130. $symbols{$w}->{xref} = $what;
  131. };
  132. }
  133. $what = $content;
  134. $label = $content;
  135. $new_what = 1;
  136. }
  137. push @labels, [($content, $label)];
  138. $tag = $new_tag;
  139. push @{$data{$nametag}->{symbols}}, $content if ($data{$nametag}->{what});
  140. next;
  141. }
  142. if ($tag ne "" && $new_tag) {
  143. $tag = $new_tag;
  144. if ($new_what) {
  145. @{$data{$what}->{label_list}} = @labels if ($data{$nametag}->{what});
  146. @labels = ();
  147. $label = "";
  148. $new_what = 0;
  149. $data{$what}->{type} = $type;
  150. if (!defined($data{$what}->{file})) {
  151. $data{$what}->{file} = $name;
  152. $data{$what}->{filepath} = $file;
  153. } else {
  154. $data{$what}->{description} .= "\n\n" if (defined($data{$what}->{description}));
  155. if ($name ne $data{$what}->{file}) {
  156. $data{$what}->{file} .= " " . $name;
  157. $data{$what}->{filepath} .= " " . $file;
  158. }
  159. }
  160. print STDERR "\twhat: $what\n" if ($debug & $dbg_what_parsing);
  161. $data{$what}->{line_no} = $ln;
  162. } else {
  163. $data{$what}->{line_no} = $ln if (!defined($data{$what}->{line_no}));
  164. }
  165. if (!$what) {
  166. parse_error($file, $ln, "'What:' should come first:", $_);
  167. next;
  168. }
  169. if ($new_tag eq "description") {
  170. $sep =~ s,:, ,;
  171. $content = ' ' x length($new_tag) . $sep . $content;
  172. while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
  173. if ($content =~ m/^(\s*)(\S.*)$/) {
  174. # Preserve initial spaces for the first line
  175. $space = $1;
  176. $content = "$2\n";
  177. $data{$what}->{$tag} .= $content;
  178. } else {
  179. undef($space);
  180. }
  181. } else {
  182. $data{$what}->{$tag} = $content;
  183. }
  184. next;
  185. }
  186. }
  187. # Store any contents before tags at the database
  188. if (!$tag && $data{$nametag}->{what}) {
  189. $data{$nametag}->{description} .= $_;
  190. next;
  191. }
  192. if ($tag eq "description") {
  193. my $content = $_;
  194. while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
  195. if (m/^\s*\n/) {
  196. $data{$what}->{$tag} .= "\n";
  197. next;
  198. }
  199. if (!defined($space)) {
  200. # Preserve initial spaces for the first line
  201. if ($content =~ m/^(\s*)(\S.*)$/) {
  202. $space = $1;
  203. $content = "$2\n";
  204. }
  205. } else {
  206. $space = "" if (!($content =~ s/^($space)//));
  207. }
  208. $data{$what}->{$tag} .= $content;
  209. next;
  210. }
  211. if (m/^\s*(.*)/) {
  212. $data{$what}->{$tag} .= "\n$1";
  213. $data{$what}->{$tag} =~ s/\n+$//;
  214. next;
  215. }
  216. # Everything else is error
  217. parse_error($file, $ln, "Unexpected content", $_);
  218. }
  219. $data{$nametag}->{description} =~ s/^\n+// if ($data{$nametag}->{description});
  220. if ($what) {
  221. parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description});
  222. foreach my $w(split /\xac/,$what) {
  223. $symbols{$w}->{xref} = $what;
  224. };
  225. }
  226. close IN;
  227. }
  228. sub create_labels {
  229. my %labels;
  230. foreach my $what (keys %data) {
  231. next if ($data{$what}->{file} eq "File");
  232. foreach my $p (@{$data{$what}->{label_list}}) {
  233. my ($content, $label) = @{$p};
  234. $label = "abi_" . $label . " ";
  235. $label =~ tr/A-Z/a-z/;
  236. # Convert special chars to "_"
  237. $label =~s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g;
  238. $label =~ s,_+,_,g;
  239. $label =~ s,_$,,;
  240. # Avoid duplicated labels
  241. while (defined($labels{$label})) {
  242. my @chars = ("A".."Z", "a".."z");
  243. $label .= $chars[rand @chars];
  244. }
  245. $labels{$label} = 1;
  246. $data{$what}->{label} = $label;
  247. # only one label is enough
  248. last;
  249. }
  250. }
  251. }
  252. #
  253. # Outputs the book on ReST format
  254. #
  255. # \b doesn't work well with paths. So, we need to define something else:
  256. # Boundaries are punct characters, spaces and end-of-line
  257. my $start = qr {(^|\s|\() }x;
  258. my $bondary = qr { ([,.:;\)\s]|\z) }x;
  259. my $xref_match = qr { $start(\/(sys|config|proc|dev|kvd)\/[^,.:;\)\s]+)$bondary }x;
  260. my $symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x2f\x3a-\x40\x7b-\xff]) }x;
  261. sub output_rest {
  262. create_labels();
  263. my $part = "";
  264. foreach my $what (sort {
  265. ($data{$a}->{type} eq "File") cmp ($data{$b}->{type} eq "File") ||
  266. $a cmp $b
  267. } keys %data) {
  268. my $type = $data{$what}->{type};
  269. my @file = split / /, $data{$what}->{file};
  270. my @filepath = split / /, $data{$what}->{filepath};
  271. if ($enable_lineno) {
  272. printf ".. LINENO %s%s#%s\n\n",
  273. $prefix, $file[0],
  274. $data{$what}->{line_no};
  275. }
  276. my $w = $what;
  277. if ($type ne "File") {
  278. my $cur_part = $what;
  279. if ($what =~ '/') {
  280. if ($what =~ m#^(\/?(?:[\w\-]+\/?){1,2})#) {
  281. $cur_part = "Symbols under $1";
  282. $cur_part =~ s,/$,,;
  283. }
  284. }
  285. if ($cur_part ne "" && $part ne $cur_part) {
  286. $part = $cur_part;
  287. my $bar = $part;
  288. $bar =~ s/./-/g;
  289. print "$part\n$bar\n\n";
  290. }
  291. printf ".. _%s:\n\n", $data{$what}->{label};
  292. my @names = split /\xac/,$w;
  293. my $len = 0;
  294. foreach my $name (@names) {
  295. $name =~ s/$symbols/\\$1/g;
  296. $name = "**$name**";
  297. $len = length($name) if (length($name) > $len);
  298. }
  299. print "+-" . "-" x $len . "-+\n";
  300. foreach my $name (@names) {
  301. printf "| %s", $name . " " x ($len - length($name)) . " |\n";
  302. print "+-" . "-" x $len . "-+\n";
  303. }
  304. print "\n";
  305. }
  306. for (my $i = 0; $i < scalar(@filepath); $i++) {
  307. my $path = $filepath[$i];
  308. my $f = $file[$i];
  309. $path =~ s,.*/(.*/.*),$1,;;
  310. $path =~ s,[/\-],_,g;;
  311. my $fileref = "abi_file_".$path;
  312. if ($type eq "File") {
  313. print ".. _$fileref:\n\n";
  314. } else {
  315. print "Defined on file :ref:`$f <$fileref>`\n\n";
  316. }
  317. }
  318. if ($type eq "File") {
  319. my $bar = $w;
  320. $bar =~ s/./-/g;
  321. print "$w\n$bar\n\n";
  322. }
  323. my $desc = "";
  324. $desc = $data{$what}->{description} if (defined($data{$what}->{description}));
  325. $desc =~ s/\s+$/\n/;
  326. if (!($desc =~ /^\s*$/)) {
  327. if ($description_is_rst) {
  328. # Remove title markups from the description
  329. # Having titles inside ABI files will only work if extra
  330. # care would be taken in order to strictly follow the same
  331. # level order for each markup.
  332. $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g;
  333. # Enrich text by creating cross-references
  334. my $new_desc = "";
  335. my $init_indent = -1;
  336. my $literal_indent = -1;
  337. open(my $fh, "+<", \$desc);
  338. while (my $d = <$fh>) {
  339. my $indent = $d =~ m/^(\s+)/;
  340. my $spaces = length($indent);
  341. $init_indent = $indent if ($init_indent < 0);
  342. if ($literal_indent >= 0) {
  343. if ($spaces > $literal_indent) {
  344. $new_desc .= $d;
  345. next;
  346. } else {
  347. $literal_indent = -1;
  348. }
  349. } else {
  350. if ($d =~ /()::$/ && !($d =~ /^\s*\.\./)) {
  351. $literal_indent = $spaces;
  352. }
  353. }
  354. $d =~ s,Documentation/(?!devicetree)(\S+)\.rst,:doc:`/$1`,g;
  355. my @matches = $d =~ m,Documentation/ABI/([\w\/\-]+),g;
  356. foreach my $f (@matches) {
  357. my $xref = $f;
  358. my $path = $f;
  359. $path =~ s,.*/(.*/.*),$1,;;
  360. $path =~ s,[/\-],_,g;;
  361. $xref .= " <abi_file_" . $path . ">";
  362. $d =~ s,\bDocumentation/ABI/$f\b,:ref:`$xref`,g;
  363. }
  364. # Seek for cross reference symbols like /sys/...
  365. @matches = $d =~ m/$xref_match/g;
  366. foreach my $s (@matches) {
  367. next if (!($s =~ m,/,));
  368. if (defined($data{$s}) && defined($data{$s}->{label})) {
  369. my $xref = $s;
  370. $xref =~ s/$symbols/\\$1/g;
  371. $xref = ":ref:`$xref <" . $data{$s}->{label} . ">`";
  372. $d =~ s,$start$s$bondary,$1$xref$2,g;
  373. }
  374. }
  375. $new_desc .= $d;
  376. }
  377. close $fh;
  378. print "$new_desc\n\n";
  379. } else {
  380. $desc =~ s/^\s+//;
  381. # Remove title markups from the description, as they won't work
  382. $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g;
  383. if ($desc =~ m/\:\n/ || $desc =~ m/\n[\t ]+/ || $desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/) {
  384. # put everything inside a code block
  385. $desc =~ s/\n/\n /g;
  386. print "::\n\n";
  387. print " $desc\n\n";
  388. } else {
  389. # Escape any special chars from description
  390. $desc =~s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g;
  391. print "$desc\n\n";
  392. }
  393. }
  394. } else {
  395. print "DESCRIPTION MISSING for $what\n\n" if (!$data{$what}->{is_file});
  396. }
  397. if ($data{$what}->{symbols}) {
  398. printf "Has the following ABI:\n\n";
  399. foreach my $content(@{$data{$what}->{symbols}}) {
  400. my $label = $data{$symbols{$content}->{xref}}->{label};
  401. # Escape special chars from content
  402. $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g;
  403. print "- :ref:`$content <$label>`\n\n";
  404. }
  405. }
  406. if (defined($data{$what}->{users})) {
  407. my $users = $data{$what}->{users};
  408. $users =~ s/\n/\n\t/g;
  409. printf "Users:\n\t%s\n\n", $users if ($users ne "");
  410. }
  411. }
  412. }
  413. #
  414. # Searches for ABI symbols
  415. #
  416. sub search_symbols {
  417. foreach my $what (sort keys %data) {
  418. next if (!($what =~ m/($arg)/));
  419. my $type = $data{$what}->{type};
  420. next if ($type eq "File");
  421. my $file = $data{$what}->{filepath};
  422. $what =~ s/\xac/, /g;
  423. my $bar = $what;
  424. $bar =~ s/./-/g;
  425. print "\n$what\n$bar\n\n";
  426. my $kernelversion = $data{$what}->{kernelversion} if (defined($data{$what}->{kernelversion}));
  427. my $contact = $data{$what}->{contact} if (defined($data{$what}->{contact}));
  428. my $users = $data{$what}->{users} if (defined($data{$what}->{users}));
  429. my $date = $data{$what}->{date} if (defined($data{$what}->{date}));
  430. my $desc = $data{$what}->{description} if (defined($data{$what}->{description}));
  431. $kernelversion =~ s/^\s+// if ($kernelversion);
  432. $contact =~ s/^\s+// if ($contact);
  433. if ($users) {
  434. $users =~ s/^\s+//;
  435. $users =~ s/\n//g;
  436. }
  437. $date =~ s/^\s+// if ($date);
  438. $desc =~ s/^\s+// if ($desc);
  439. printf "Kernel version:\t\t%s\n", $kernelversion if ($kernelversion);
  440. printf "Date:\t\t\t%s\n", $date if ($date);
  441. printf "Contact:\t\t%s\n", $contact if ($contact);
  442. printf "Users:\t\t\t%s\n", $users if ($users);
  443. print "Defined on file(s):\t$file\n\n";
  444. print "Description:\n\n$desc";
  445. }
  446. }
  447. # Exclude /sys/kernel/debug and /sys/kernel/tracing from the search path
  448. sub dont_parse_special_attributes {
  449. if (($File::Find::dir =~ m,^/sys/kernel,)) {
  450. return grep {!/(debug|tracing)/ } @_;
  451. }
  452. if (($File::Find::dir =~ m,^/sys/fs,)) {
  453. return grep {!/(pstore|bpf|fuse)/ } @_;
  454. }
  455. return @_
  456. }
  457. my %leaf;
  458. my %aliases;
  459. my @files;
  460. my %root;
  461. sub graph_add_file {
  462. my $file = shift;
  463. my $type = shift;
  464. my $dir = $file;
  465. $dir =~ s,^(.*/).*,$1,;
  466. $file =~ s,.*/,,;
  467. my $name;
  468. my $file_ref = \%root;
  469. foreach my $edge(split "/", $dir) {
  470. $name .= "$edge/";
  471. if (!defined ${$file_ref}{$edge}) {
  472. ${$file_ref}{$edge} = { };
  473. }
  474. $file_ref = \%{$$file_ref{$edge}};
  475. ${$file_ref}{"__name"} = [ $name ];
  476. }
  477. $name .= "$file";
  478. ${$file_ref}{$file} = {
  479. "__name" => [ $name ]
  480. };
  481. return \%{$$file_ref{$file}};
  482. }
  483. sub graph_add_link {
  484. my $file = shift;
  485. my $link = shift;
  486. # Traverse graph to find the reference
  487. my $file_ref = \%root;
  488. foreach my $edge(split "/", $file) {
  489. $file_ref = \%{$$file_ref{$edge}} || die "Missing node!";
  490. }
  491. # do a BFS
  492. my @queue;
  493. my %seen;
  494. my $st;
  495. push @queue, $file_ref;
  496. $seen{$start}++;
  497. while (@queue) {
  498. my $v = shift @queue;
  499. my @child = keys(%{$v});
  500. foreach my $c(@child) {
  501. next if $seen{$$v{$c}};
  502. next if ($c eq "__name");
  503. if (!defined($$v{$c}{"__name"})) {
  504. printf STDERR "Error: Couldn't find a non-empty name on a children of $file/.*: ";
  505. print STDERR Dumper(%{$v});
  506. exit;
  507. }
  508. # Add new name
  509. my $name = @{$$v{$c}{"__name"}}[0];
  510. if ($name =~ s#^$file/#$link/#) {
  511. push @{$$v{$c}{"__name"}}, $name;
  512. }
  513. # Add child to the queue and mark as seen
  514. push @queue, $$v{$c};
  515. $seen{$c}++;
  516. }
  517. }
  518. }
  519. my $escape_symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x29\x2b-\x2d\x3a-\x40\x7b-\xfe]) }x;
  520. sub parse_existing_sysfs {
  521. my $file = $File::Find::name;
  522. my $mode = (lstat($file))[2];
  523. my $abs_file = abs_path($file);
  524. my @tmp;
  525. push @tmp, $file;
  526. push @tmp, $abs_file if ($abs_file ne $file);
  527. foreach my $f(@tmp) {
  528. # Ignore cgroup, as this is big and has zero docs under ABI
  529. return if ($f =~ m#^/sys/fs/cgroup/#);
  530. # Ignore firmware as it is documented elsewhere
  531. # Either ACPI or under Documentation/devicetree/bindings/
  532. return if ($f =~ m#^/sys/firmware/#);
  533. # Ignore some sysfs nodes that aren't actually part of ABI
  534. return if ($f =~ m#/sections|notes/#);
  535. # Would need to check at
  536. # Documentation/admin-guide/kernel-parameters.txt, but this
  537. # is not easily parseable.
  538. return if ($f =~ m#/parameters/#);
  539. }
  540. if (S_ISLNK($mode)) {
  541. $aliases{$file} = $abs_file;
  542. return;
  543. }
  544. return if (S_ISDIR($mode));
  545. # Trivial: file is defined exactly the same way at ABI What:
  546. return if (defined($data{$file}));
  547. return if (defined($data{$abs_file}));
  548. push @files, graph_add_file($abs_file, "file");
  549. }
  550. sub get_leave($)
  551. {
  552. my $what = shift;
  553. my $leave;
  554. my $l = $what;
  555. my $stop = 1;
  556. $leave = $l;
  557. $leave =~ s,/$,,;
  558. $leave =~ s,.*/,,;
  559. $leave =~ s/[\(\)]//g;
  560. # $leave is used to improve search performance at
  561. # check_undefined_symbols, as the algorithm there can seek
  562. # for a small number of "what". It also allows giving a
  563. # hint about a leave with the same name somewhere else.
  564. # However, there are a few occurences where the leave is
  565. # either a wildcard or a number. Just group such cases
  566. # altogether.
  567. if ($leave =~ m/\.\*/ || $leave eq "" || $leave =~ /\\d/) {
  568. $leave = "others";
  569. }
  570. return $leave;
  571. }
  572. my @not_found;
  573. sub check_file($$)
  574. {
  575. my $file_ref = shift;
  576. my $names_ref = shift;
  577. my @names = @{$names_ref};
  578. my $file = $names[0];
  579. my $found_string;
  580. my $leave = get_leave($file);
  581. if (!defined($leaf{$leave})) {
  582. $leave = "others";
  583. }
  584. my @expr = @{$leaf{$leave}->{expr}};
  585. die ("\rmissing rules for $leave") if (!defined($leaf{$leave}));
  586. my $path = $file;
  587. $path =~ s,(.*/).*,$1,;
  588. if ($search_string) {
  589. return if (!($file =~ m#$search_string#));
  590. $found_string = 1;
  591. }
  592. for (my $i = 0; $i < @names; $i++) {
  593. if ($found_string && $hint) {
  594. if (!$i) {
  595. print STDERR "--> $names[$i]\n";
  596. } else {
  597. print STDERR " $names[$i]\n";
  598. }
  599. }
  600. foreach my $re (@expr) {
  601. print STDERR "$names[$i] =~ /^$re\$/\n" if ($debug && $dbg_undefined);
  602. if ($names[$i] =~ $re) {
  603. return;
  604. }
  605. }
  606. }
  607. if ($leave ne "others") {
  608. my @expr = @{$leaf{"others"}->{expr}};
  609. for (my $i = 0; $i < @names; $i++) {
  610. foreach my $re (@expr) {
  611. print STDERR "$names[$i] =~ /^$re\$/\n" if ($debug && $dbg_undefined);
  612. if ($names[$i] =~ $re) {
  613. return;
  614. }
  615. }
  616. }
  617. }
  618. push @not_found, $file if (!$search_string || $found_string);
  619. if ($hint && (!$search_string || $found_string)) {
  620. my $what = $leaf{$leave}->{what};
  621. $what =~ s/\xac/\n\t/g;
  622. if ($leave ne "others") {
  623. print STDERR "\r more likely regexes:\n\t$what\n";
  624. } else {
  625. print STDERR "\r tested regexes:\n\t$what\n";
  626. }
  627. }
  628. }
  629. sub check_undefined_symbols {
  630. my $num_files = scalar @files;
  631. my $next_i = 0;
  632. my $start_time = times;
  633. @files = sort @files;
  634. my $last_time = $start_time;
  635. # When either debug or hint is enabled, there's no sense showing
  636. # progress, as the progress will be overriden.
  637. if ($hint || ($debug && $dbg_undefined)) {
  638. $next_i = $num_files;
  639. }
  640. my $is_console;
  641. $is_console = 1 if (-t STDERR);
  642. for (my $i = 0; $i < $num_files; $i++) {
  643. my $file_ref = $files[$i];
  644. my @names = @{$$file_ref{"__name"}};
  645. check_file($file_ref, \@names);
  646. my $cur_time = times;
  647. if ($i == $next_i || $cur_time > $last_time + 1) {
  648. my $percent = $i * 100 / $num_files;
  649. my $tm = $cur_time - $start_time;
  650. my $time = sprintf "%d:%02d", int($tm), 60 * ($tm - int($tm));
  651. printf STDERR "\33[2K\r", if ($is_console);
  652. printf STDERR "%s: processing sysfs files... %i%%: $names[0]", $time, $percent;
  653. printf STDERR "\n", if (!$is_console);
  654. STDERR->flush();
  655. $next_i = int (($percent + 1) * $num_files / 100);
  656. $last_time = $cur_time;
  657. }
  658. }
  659. my $cur_time = times;
  660. my $tm = $cur_time - $start_time;
  661. my $time = sprintf "%d:%02d", int($tm), 60 * ($tm - int($tm));
  662. printf STDERR "\33[2K\r", if ($is_console);
  663. printf STDERR "%s: processing sysfs files... done\n", $time;
  664. foreach my $file (@not_found) {
  665. print "$file not found.\n";
  666. }
  667. }
  668. sub undefined_symbols {
  669. print STDERR "Reading $sysfs_prefix directory contents...";
  670. find({
  671. wanted =>\&parse_existing_sysfs,
  672. preprocess =>\&dont_parse_special_attributes,
  673. no_chdir => 1
  674. }, $sysfs_prefix);
  675. print STDERR "done.\n";
  676. $leaf{"others"}->{what} = "";
  677. print STDERR "Converting ABI What fields into regexes...";
  678. foreach my $w (sort keys %data) {
  679. foreach my $what (split /\xac/,$w) {
  680. next if (!($what =~ m/^$sysfs_prefix/));
  681. # Convert what into regular expressions
  682. # Escape dot characters
  683. $what =~ s/\./\xf6/g;
  684. # Temporarily change [0-9]+ type of patterns
  685. $what =~ s/\[0\-9\]\+/\xff/g;
  686. # Temporarily change [\d+-\d+] type of patterns
  687. $what =~ s/\[0\-\d+\]/\xff/g;
  688. $what =~ s/\[(\d+)\]/\xf4$1\xf5/g;
  689. # Temporarily change [0-9] type of patterns
  690. $what =~ s/\[(\d)\-(\d)\]/\xf4$1-$2\xf5/g;
  691. # Handle multiple option patterns
  692. $what =~ s/[\{\<\[]([\w_]+)(?:[,|]+([\w_]+)){1,}[\}\>\]]/($1|$2)/g;
  693. # Handle wildcards
  694. $what =~ s,\*,.*,g;
  695. $what =~ s,/\xf6..,/.*,g;
  696. $what =~ s/\<[^\>]+\>/.*/g;
  697. $what =~ s/\{[^\}]+\}/.*/g;
  698. $what =~ s/\[[^\]]+\]/.*/g;
  699. $what =~ s/[XYZ]/.*/g;
  700. # Recover [0-9] type of patterns
  701. $what =~ s/\xf4/[/g;
  702. $what =~ s/\xf5/]/g;
  703. # Remove duplicated spaces
  704. $what =~ s/\s+/ /g;
  705. # Special case: this ABI has a parenthesis on it
  706. $what =~ s/sqrt\(x^2\+y^2\+z^2\)/sqrt\(x^2\+y^2\+z^2\)/;
  707. # Special case: drop comparition as in:
  708. # What: foo = <something>
  709. # (this happens on a few IIO definitions)
  710. $what =~ s,\s*\=.*$,,;
  711. # Escape all other symbols
  712. $what =~ s/$escape_symbols/\\$1/g;
  713. $what =~ s/\\\\/\\/g;
  714. $what =~ s/\\([\[\]\(\)\|])/$1/g;
  715. $what =~ s/(\d+)\\(-\d+)/$1$2/g;
  716. $what =~ s/\xff/\\d+/g;
  717. # Special case: IIO ABI which a parenthesis.
  718. $what =~ s/sqrt(.*)/sqrt\(.*\)/;
  719. # Simplify regexes with multiple .*
  720. $what =~ s#(?:\.\*){2,}##g;
  721. # $what =~ s#\.\*/\.\*#.*#g;
  722. # Recover dot characters
  723. $what =~ s/\xf6/\./g;
  724. my $leave = get_leave($what);
  725. my $added = 0;
  726. foreach my $l (split /\|/, $leave) {
  727. if (defined($leaf{$l})) {
  728. next if ($leaf{$l}->{what} =~ m/\b$what\b/);
  729. $leaf{$l}->{what} .= "\xac" . $what;
  730. $added = 1;
  731. } else {
  732. $leaf{$l}->{what} = $what;
  733. $added = 1;
  734. }
  735. }
  736. if ($search_string && $added) {
  737. print STDERR "What: $what\n" if ($what =~ m#$search_string#);
  738. }
  739. }
  740. }
  741. # Compile regexes
  742. foreach my $l (sort keys %leaf) {
  743. my @expr;
  744. foreach my $w(sort split /\xac/, $leaf{$l}->{what}) {
  745. push @expr, qr /^$w$/;
  746. }
  747. $leaf{$l}->{expr} = \@expr;
  748. }
  749. # Take links into account
  750. foreach my $link (sort keys %aliases) {
  751. my $abs_file = $aliases{$link};
  752. graph_add_link($abs_file, $link);
  753. }
  754. print STDERR "done.\n";
  755. check_undefined_symbols;
  756. }
  757. # Ensure that the prefix will always end with a slash
  758. # While this is not needed for find, it makes the patch nicer
  759. # with --enable-lineno
  760. $prefix =~ s,/?$,/,;
  761. if ($cmd eq "undefined" || $cmd eq "search") {
  762. $show_warnings = 0;
  763. }
  764. #
  765. # Parses all ABI files located at $prefix dir
  766. #
  767. find({wanted =>\&parse_abi, no_chdir => 1}, $prefix);
  768. print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug & $dbg_dump_abi_structs);
  769. #
  770. # Handles the command
  771. #
  772. if ($cmd eq "undefined") {
  773. undefined_symbols;
  774. } elsif ($cmd eq "search") {
  775. search_symbols;
  776. } else {
  777. if ($cmd eq "rest") {
  778. output_rest;
  779. }
  780. # Warn about duplicated ABI entries
  781. foreach my $what(sort keys %symbols) {
  782. my @files = @{$symbols{$what}->{file}};
  783. next if (scalar(@files) == 1);
  784. printf STDERR "Warning: $what is defined %d times: @files\n",
  785. scalar(@files);
  786. }
  787. }
  788. __END__
  789. =head1 NAME
  790. get_abi.pl - parse the Linux ABI files and produce a ReST book.
  791. =head1 SYNOPSIS
  792. B<get_abi.pl> [--debug <level>] [--enable-lineno] [--man] [--help]
  793. [--(no-)rst-source] [--dir=<dir>] [--show-hints]
  794. [--search-string <regex>]
  795. <COMMAND> [<ARGUMENT>]
  796. Where B<COMMAND> can be:
  797. =over 8
  798. B<search> I<SEARCH_REGEX> - search for I<SEARCH_REGEX> inside ABI
  799. B<rest> - output the ABI in ReST markup language
  800. B<validate> - validate the ABI contents
  801. B<undefined> - existing symbols at the system that aren't
  802. defined at Documentation/ABI
  803. =back
  804. =head1 OPTIONS
  805. =over 8
  806. =item B<--dir>
  807. Changes the location of the ABI search. By default, it uses
  808. the Documentation/ABI directory.
  809. =item B<--rst-source> and B<--no-rst-source>
  810. The input file may be using ReST syntax or not. Those two options allow
  811. selecting between a rst-compliant source ABI (B<--rst-source>), or a
  812. plain text that may be violating ReST spec, so it requres some escaping
  813. logic (B<--no-rst-source>).
  814. =item B<--enable-lineno>
  815. Enable output of .. LINENO lines.
  816. =item B<--debug> I<debug level>
  817. Print debug information according with the level, which is given by the
  818. following bitmask:
  819. - 1: Debug parsing What entries from ABI files;
  820. - 2: Shows what files are opened from ABI files;
  821. - 4: Dump the structs used to store the contents of the ABI files.
  822. =item B<--show-hints>
  823. Show hints about possible definitions for the missing ABI symbols.
  824. Used only when B<undefined>.
  825. =item B<--search-string> I<regex string>
  826. Show only occurences that match a search string.
  827. Used only when B<undefined>.
  828. =item B<--help>
  829. Prints a brief help message and exits.
  830. =item B<--man>
  831. Prints the manual page and exits.
  832. =back
  833. =head1 DESCRIPTION
  834. Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI),
  835. allowing to search for ABI symbols or to produce a ReST book containing
  836. the Linux ABI documentation.
  837. =head1 EXAMPLES
  838. Search for all stable symbols with the word "usb":
  839. =over 8
  840. $ scripts/get_abi.pl search usb --dir Documentation/ABI/stable
  841. =back
  842. Search for all symbols that match the regex expression "usb.*cap":
  843. =over 8
  844. $ scripts/get_abi.pl search usb.*cap
  845. =back
  846. Output all obsoleted symbols in ReST format
  847. =over 8
  848. $ scripts/get_abi.pl rest --dir Documentation/ABI/obsolete
  849. =back
  850. =head1 BUGS
  851. Report bugs to Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
  852. =head1 COPYRIGHT
  853. Copyright (c) 2016-2021 by Mauro Carvalho Chehab <mchehab+huawei@kernel.org>.
  854. License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
  855. This is free software: you are free to change and redistribute it.
  856. There is NO WARRANTY, to the extent permitted by law.
  857. =cut