From 102a0743326a03cd1a1202ceda21e175b7d3575c Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 20 Feb 2024 01:20:52 +0000
Subject: [PATCH] add new system file

---
 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