From ea08eeccae9297f7aabd2ef7f0c2517ac4549acc Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 20 Feb 2024 01:18:26 +0000
Subject: [PATCH] write in 30M
---
kernel/scripts/generate_initcall_order.pl | 320 ++++++++++++++++++++++++++++-------------------------
1 files changed, 170 insertions(+), 150 deletions(-)
diff --git a/kernel/scripts/generate_initcall_order.pl b/kernel/scripts/generate_initcall_order.pl
index bbae939..1a88d3f 100755
--- a/kernel/scripts/generate_initcall_order.pl
+++ b/kernel/scripts/generate_initcall_order.pl
@@ -8,22 +8,22 @@
use strict;
use warnings;
use IO::Handle;
+use IO::Select;
+use POSIX ":sys_wait_h";
-my $nm = $ENV{'LLVM_NM'} || "llvm-nm";
-my $ar = $ENV{'AR'} || "llvm-ar";
-my $objtree = $ENV{'objtree'} || ".";
+my $nm = $ENV{'NM'} || die "$0: ERROR: NM not set?";
+my $objtree = $ENV{'objtree'} || '.';
-## list of all object files to process, in link order
-my @objects;
## currently active child processes
my $jobs = {}; # child process pid -> file handle
## results from child processes
-my $results = {}; # object index -> { level, function }
+my $results = {}; # object index -> [ { level, secname }, ... ]
-## reads _NPROCESSORS_ONLN to determine the number of processes to start
+## reads _NPROCESSORS_ONLN to determine the maximum number of processes to
+## start
sub get_online_processors {
open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |")
- or die "$0: failed to execute getconf: $!";
+ or die "$0: ERROR: failed to execute getconf: $!";
my $procs = <$fh>;
close($fh);
@@ -34,217 +34,237 @@
return int($procs);
}
-## finds initcalls defined in an object file, parses level and function name,
-## and prints it out to the parent process
+## writes results to the parent process
+## format: <file index> <initcall level> <base initcall section name>
+sub write_results {
+ my ($index, $initcalls) = @_;
+
+ # sort by the counter value to ensure the order of initcalls within
+ # each object file is correct
+ foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) {
+ my $level = $initcalls->{$counter}->{'level'};
+
+ # section name for the initcall function
+ my $secname = $initcalls->{$counter}->{'module'} . '__' .
+ $counter . '_' .
+ $initcalls->{$counter}->{'line'} . '_' .
+ $initcalls->{$counter}->{'function'};
+
+ print "$index $level $secname\n";
+ }
+}
+
+## reads a result line from a child process and adds it to the $results array
+sub read_results{
+ my ($fh) = @_;
+
+ # each child prints out a full line w/ autoflush and exits after the
+ # last line, so even if buffered I/O blocks here, it shouldn't block
+ # very long
+ my $data = <$fh>;
+
+ if (!defined($data)) {
+ return 0;
+ }
+
+ chomp($data);
+
+ my ($index, $level, $secname) = $data =~
+ /^(\d+)\ ([^\ ]+)\ (.*)$/;
+
+ if (!defined($index) ||
+ !defined($level) ||
+ !defined($secname)) {
+ die "$0: ERROR: child process returned invalid data: $data\n";
+ }
+
+ $index = int($index);
+
+ if (!exists($results->{$index})) {
+ $results->{$index} = [];
+ }
+
+ push (@{$results->{$index}}, {
+ 'level' => $level,
+ 'secname' => $secname
+ });
+
+ return 1;
+}
+
+## finds initcalls from an object file or all object files in an archive, and
+## writes results back to the parent process
sub find_initcalls {
- my ($object) = @_;
+ my ($index, $file) = @_;
- die "$0: object file $object doesn't exist?" if (! -f $object);
+ die "$0: ERROR: file $file doesn't exist?" if (! -f $file);
- open(my $fh, "\"$nm\" --just-symbol-name --defined-only \"$object\" 2>/dev/null |")
- or die "$0: failed to execute \"$nm\": $!";
+ open(my $fh, "\"$nm\" --defined-only \"$file\" 2>/dev/null |")
+ or die "$0: ERROR: failed to execute \"$nm\": $!";
my $initcalls = {};
while (<$fh>) {
chomp;
- my ($counter, $line, $symbol) = $_ =~ /^__initcall_(\d+)_(\d+)_(.*)$/;
+ # check for the start of a new object file (if processing an
+ # archive)
+ my ($path)= $_ =~ /^(.+)\:$/;
- if (!defined($counter) || !defined($line) || !defined($symbol)) {
+ if (defined($path)) {
+ write_results($index, $initcalls);
+ $initcalls = {};
next;
}
- my ($function, $level) = $symbol =~
- /^(.*)((early|rootfs|con|security|[0-9])s?)$/;
+ # look for an initcall
+ my ($module, $counter, $line, $symbol) = $_ =~
+ /[a-z]\s+__initcall__(\S*)__(\d+)_(\d+)_(.*)$/;
- die "$0: duplicate initcall counter value in object $object: $_"
- if exists($initcalls->{$counter});
+ if (!defined($module)) {
+ $module = ''
+ }
+
+ if (!defined($counter) ||
+ !defined($line) ||
+ !defined($symbol)) {
+ next;
+ }
+
+ # parse initcall level
+ my ($function, $level) = $symbol =~
+ /^(.*)((early|rootfs|con|[0-9])s?)$/;
+
+ die "$0: ERROR: invalid initcall name $symbol in $file($path)"
+ if (!defined($function) || !defined($level));
$initcalls->{$counter} = {
- 'level' => $level,
+ 'module' => $module,
'line' => $line,
- 'function' => $function
+ 'function' => $function,
+ 'level' => $level,
};
}
close($fh);
-
- # sort initcalls in each object file numerically by the counter value
- # to ensure they are in the order they were defined
- foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) {
- print $initcalls->{$counter}->{"level"} . " " .
- $counter . " " .
- $initcalls->{$counter}->{"line"} . " " .
- $initcalls->{$counter}->{"function"} . "\n";
- }
+ write_results($index, $initcalls);
}
## waits for any child process to complete, reads the results, and adds them to
## the $results array for later processing
sub wait_for_results {
- my $pid = wait();
- if ($pid > 0) {
- my $fh = $jobs->{$pid};
+ my ($select) = @_;
- # the child process prints out results in the following format:
- # line 1: <object file index>
- # line 2..n: <level> <counter> <line> <function>
-
- my $index = <$fh>;
- chomp($index);
-
- if (!($index =~ /^\d+$/)) {
- die "$0: child $pid returned an invalid index: $index";
- }
- $index = int($index);
-
- while (<$fh>) {
- chomp;
- my ($level, $counter, $line, $function) = $_ =~
- /^([^\ ]+)\ (\d+)\ (\d+)\ (.*)$/;
-
- if (!defined($level) ||
- !defined($counter) ||
- !defined($line) ||
- !defined($function)) {
- die "$0: child $pid returned invalid data";
- }
-
- if (!exists($results->{$index})) {
- $results->{$index} = [];
- }
-
- push (@{$results->{$index}}, {
- 'level' => $level,
- 'counter' => $counter,
- 'line' => $line,
- 'function' => $function
- });
+ my $pid = 0;
+ do {
+ # unblock children that may have a full write buffer
+ foreach my $fh ($select->can_read(0)) {
+ read_results($fh);
}
- close($fh);
- delete($jobs->{$pid});
- }
+ # check for children that have exited, read the remaining data
+ # from them, and clean up
+ $pid = waitpid(-1, WNOHANG);
+ if ($pid > 0) {
+ if (!exists($jobs->{$pid})) {
+ next;
+ }
+
+ my $fh = $jobs->{$pid};
+ $select->remove($fh);
+
+ while (read_results($fh)) {
+ # until eof
+ }
+
+ close($fh);
+ delete($jobs->{$pid});
+ }
+ } while ($pid > 0);
}
-## launches child processes to find initcalls from the object files, waits for
-## each process to complete and collects the results
-sub process_objects {
- my $index = 0; # link order index of the object file
- my $njobs = get_online_processors();
+## forks a child to process each file passed in the command line and collects
+## the results
+sub process_files {
+ my $index = 0;
+ my $njobs = $ENV{'PARALLELISM'} || get_online_processors();
+ my $select = IO::Select->new();
- while (scalar(@objects) > 0) {
- my $object = shift(@objects);
-
+ while (my $file = shift(@ARGV)) {
# fork a child process and read it's stdout
my $pid = open(my $fh, '-|');
if (!defined($pid)) {
- die "$0: failed to fork: $!";
+ die "$0: ERROR: failed to fork: $!";
} elsif ($pid) {
# save the child process pid and the file handle
+ $select->add($fh);
$jobs->{$pid} = $fh;
} else {
+ # in the child process
STDOUT->autoflush(1);
- print "$index\n";
- find_initcalls("$objtree/$object");
+ find_initcalls($index, "$objtree/$file");
exit;
}
$index++;
- # if we reached the maximum number of processes, wait for one
- # to complete before launching new ones
- if (scalar(keys(%{$jobs})) >= $njobs && scalar(@objects) > 0) {
- wait_for_results();
+ # limit the number of children to $njobs
+ if (scalar(keys(%{$jobs})) >= $njobs) {
+ wait_for_results($select);
}
}
# wait for the remaining children to complete
while (scalar(keys(%{$jobs})) > 0) {
- wait_for_results();
+ wait_for_results($select);
}
}
-## gets a list of actual object files from thin archives, and adds them to
-## @objects in link order
-sub find_objects {
- while (my $file = shift(@ARGV)) {
- my $pid = open (my $fh, "\"$ar\" t \"$file\" 2>/dev/null |")
- or die "$0: failed to execute $ar: $!";
+sub generate_initcall_lds() {
+ process_files();
- my @output;
+ my $sections = {}; # level -> [ secname, ...]
- while (<$fh>) {
- chomp;
- push(@output, $_);
- }
+ # sort results to retain link order and split to sections per
+ # initcall level
+ foreach my $index (sort { $a <=> $b } keys(%{$results})) {
+ foreach my $result (@{$results->{$index}}) {
+ my $level = $result->{'level'};
- close($fh);
+ if (!exists($sections->{$level})) {
+ $sections->{$level} = [];
+ }
- # if $ar failed, assume we have an object file
- if ($? != 0) {
- push(@objects, $file);
- next;
- }
-
- # if $ar succeeded, read the list of object files
- foreach (@output) {
- push(@objects, $_);
+ push(@{$sections->{$level}}, $result->{'secname'});
}
}
-}
-## START
-find_objects();
-process_objects();
+ die "$0: ERROR: no initcalls?" if (!keys(%{$sections}));
-## process results and add them to $sections in the correct order
-my $sections = {};
+ # print out a linker script that defines the order of initcalls for
+ # each level
+ print "SECTIONS {\n";
-foreach my $index (sort { $a <=> $b } keys(%{$results})) {
- foreach my $result (@{$results->{$index}}) {
- my $level = $result->{'level'};
+ foreach my $level (sort(keys(%{$sections}))) {
+ my $section;
- if (!exists($sections->{$level})) {
- $sections->{$level} = [];
+ if ($level eq 'con') {
+ $section = '.con_initcall.init';
+ } else {
+ $section = ".initcall${level}.init";
}
- my $fsname = $result->{'counter'} . '_' .
- $result->{'line'} . '_' .
- $result->{'function'};
+ print "\t${section} : {\n";
- push(@{$sections->{$level}}, $fsname);
- }
-}
+ foreach my $secname (@{$sections->{$level}}) {
+ print "\t\t*(${section}..${secname}) ;\n";
+ }
-if (!keys(%{$sections})) {
- exit(0); # no initcalls...?
-}
-
-## print out a linker script that defines the order of initcalls for each
-## level
-print "SECTIONS {\n";
-
-foreach my $level (sort(keys(%{$sections}))) {
- my $section;
-
- if ($level eq 'con') {
- $section = '.con_initcall.init';
- } elsif ($level eq 'security') {
- $section = '.security_initcall.init';
- } else {
- $section = ".initcall${level}.init";
+ print "\t}\n";
}
- print "\t${section} : {\n";
-
- foreach my $fsname (@{$sections->{$level}}) {
- print "\t\t*(${section}..${fsname}) ;\n"
- }
-
- print "\t}\n";
+ print "}\n";
}
-print "}\n";
+generate_initcall_lds();
--
Gitblit v1.6.2