Linux Audio

Check our new training course

Loading...
v6.2
  1#!/usr/bin/env perl
  2# SPDX-License-Identifier: GPL-2.0
  3#
  4# Generates a linker script that specifies the correct initcall order.
  5#
  6# Copyright (C) 2019 Google LLC
  7
  8use strict;
  9use warnings;
 10use IO::Handle;
 11use IO::Select;
 12use POSIX ":sys_wait_h";
 13
 14my $nm = $ENV{'NM'} || die "$0: ERROR: NM not set?";
 15my $objtree = $ENV{'objtree'} || '.';
 16
 17## currently active child processes
 18my $jobs = {};		# child process pid -> file handle
 19## results from child processes
 20my $results = {};	# object index -> [ { level, secname }, ... ]
 21
 22## reads _NPROCESSORS_ONLN to determine the maximum number of processes to
 23## start
 24sub get_online_processors {
 25	open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |")
 26		or die "$0: ERROR: failed to execute getconf: $!";
 27	my $procs = <$fh>;
 28	close($fh);
 29
 30	if (!($procs =~ /^\d+$/)) {
 31		return 1;
 32	}
 33
 34	return int($procs);
 35}
 36
 37## writes results to the parent process
 38## format: <file index> <initcall level> <base initcall section name>
 39sub write_results {
 40	my ($index, $initcalls) = @_;
 41
 42	# sort by the counter value to ensure the order of initcalls within
 43	# each object file is correct
 44	foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) {
 45		my $level = $initcalls->{$counter}->{'level'};
 46
 47		# section name for the initcall function
 48		my $secname = $initcalls->{$counter}->{'module'} . '__' .
 49			      $counter . '_' .
 50			      $initcalls->{$counter}->{'line'} . '_' .
 51			      $initcalls->{$counter}->{'function'};
 52
 53		print "$index $level $secname\n";
 54	}
 55}
 56
 57## reads a result line from a child process and adds it to the $results array
 58sub read_results{
 59	my ($fh) = @_;
 60
 61	# each child prints out a full line w/ autoflush and exits after the
 62	# last line, so even if buffered I/O blocks here, it shouldn't block
 63	# very long
 64	my $data = <$fh>;
 65
 66	if (!defined($data)) {
 67		return 0;
 68	}
 69
 70	chomp($data);
 71
 72	my ($index, $level, $secname) = $data =~
 73		/^(\d+)\ ([^\ ]+)\ (.*)$/;
 74
 75	if (!defined($index) ||
 76		!defined($level) ||
 77		!defined($secname)) {
 78		die "$0: ERROR: child process returned invalid data: $data\n";
 79	}
 80
 81	$index = int($index);
 82
 83	if (!exists($results->{$index})) {
 84		$results->{$index} = [];
 85	}
 86
 87	push (@{$results->{$index}}, {
 88		'level'   => $level,
 89		'secname' => $secname
 90	});
 91
 92	return 1;
 93}
 94
 95## finds initcalls from an object file or all object files in an archive, and
 96## writes results back to the parent process
 97sub find_initcalls {
 98	my ($index, $file) = @_;
 99
100	die "$0: ERROR: file $file doesn't exist?" if (! -f $file);
101
102	open(my $fh, "\"$nm\" --defined-only \"$file\" 2>/dev/null |")
103		or die "$0: ERROR: failed to execute \"$nm\": $!";
104
105	my $initcalls = {};
106
107	while (<$fh>) {
108		chomp;
109
110		# check for the start of a new object file (if processing an
111		# archive)
112		my ($path)= $_ =~ /^(.+)\:$/;
113
114		if (defined($path)) {
115			write_results($index, $initcalls);
116			$initcalls = {};
117			next;
118		}
119
120		# look for an initcall
121		my ($module, $counter, $line, $symbol) = $_ =~
122			/[a-z]\s+__initcall__(\S*)__(\d+)_(\d+)_(.*)$/;
123
124		if (!defined($module)) {
125			$module = ''
126		}
127
128		if (!defined($counter) ||
129			!defined($line) ||
130			!defined($symbol)) {
131			next;
132		}
133
134		# parse initcall level
135		my ($function, $level) = $symbol =~
136			/^(.*)((early|rootfs|con|[0-9])s?)$/;
137
138		die "$0: ERROR: invalid initcall name $symbol in $file($path)"
139			if (!defined($function) || !defined($level));
140
141		$initcalls->{$counter} = {
142			'module'   => $module,
143			'line'     => $line,
144			'function' => $function,
145			'level'    => $level,
146		};
147	}
148
149	close($fh);
150	write_results($index, $initcalls);
151}
152
153## waits for any child process to complete, reads the results, and adds them to
154## the $results array for later processing
155sub wait_for_results {
156	my ($select) = @_;
157
158	my $pid = 0;
159	do {
160		# unblock children that may have a full write buffer
161		foreach my $fh ($select->can_read(0)) {
162			read_results($fh);
163		}
164
165		# check for children that have exited, read the remaining data
166		# from them, and clean up
167		$pid = waitpid(-1, WNOHANG);
168		if ($pid > 0) {
169			if (!exists($jobs->{$pid})) {
170				next;
171			}
172
173			my $fh = $jobs->{$pid};
174			$select->remove($fh);
175
176			while (read_results($fh)) {
177				# until eof
178			}
179
180			close($fh);
181			delete($jobs->{$pid});
182		}
183	} while ($pid > 0);
184}
185
186## forks a child to process each file passed in the command line and collects
187## the results
188sub process_files {
189	my $index = 0;
190	my $njobs = $ENV{'PARALLELISM'} || get_online_processors();
191	my $select = IO::Select->new();
192
193	while (my $file = shift(@ARGV)) {
194		# fork a child process and read it's stdout
195		my $pid = open(my $fh, '-|');
196
197		if (!defined($pid)) {
198			die "$0: ERROR: failed to fork: $!";
199		} elsif ($pid) {
200			# save the child process pid and the file handle
201			$select->add($fh);
202			$jobs->{$pid} = $fh;
203		} else {
204			# in the child process
205			STDOUT->autoflush(1);
206			find_initcalls($index, "$objtree/$file");
207			exit;
208		}
209
210		$index++;
211
212		# limit the number of children to $njobs
213		if (scalar(keys(%{$jobs})) >= $njobs) {
214			wait_for_results($select);
215		}
216	}
217
218	# wait for the remaining children to complete
219	while (scalar(keys(%{$jobs})) > 0) {
220		wait_for_results($select);
221	}
222}
223
224sub generate_initcall_lds() {
225	process_files();
226
227	my $sections = {};	# level -> [ secname, ...]
228
229	# sort results to retain link order and split to sections per
230	# initcall level
231	foreach my $index (sort { $a <=> $b } keys(%{$results})) {
232		foreach my $result (@{$results->{$index}}) {
233			my $level = $result->{'level'};
234
235			if (!exists($sections->{$level})) {
236				$sections->{$level} = [];
237			}
238
239			push(@{$sections->{$level}}, $result->{'secname'});
240		}
241	}
242
243	die "$0: ERROR: no initcalls?" if (!keys(%{$sections}));
244
245	# print out a linker script that defines the order of initcalls for
246	# each level
247	print "SECTIONS {\n";
248
249	foreach my $level (sort(keys(%{$sections}))) {
250		my $section;
251
252		if ($level eq 'con') {
253			$section = '.con_initcall.init';
254		} else {
255			$section = ".initcall${level}.init";
256		}
257
258		print "\t${section} : {\n";
259
260		foreach my $secname (@{$sections->{$level}}) {
261			print "\t\t*(${section}..${secname}) ;\n";
262		}
263
264		print "\t}\n";
265	}
266
267	print "}\n";
268}
269
270generate_initcall_lds();
v6.9.4
  1#!/usr/bin/env perl
  2# SPDX-License-Identifier: GPL-2.0
  3#
  4# Generates a linker script that specifies the correct initcall order.
  5#
  6# Copyright (C) 2019 Google LLC
  7
  8use strict;
  9use warnings;
 10use IO::Handle;
 11use IO::Select;
 12use POSIX ":sys_wait_h";
 13
 14my $nm = $ENV{'NM'} || die "$0: ERROR: NM not set?";
 15my $objtree = $ENV{'objtree'} || '.';
 16
 17## currently active child processes
 18my $jobs = {};		# child process pid -> file handle
 19## results from child processes
 20my $results = {};	# object index -> [ { level, secname }, ... ]
 21
 22## reads _NPROCESSORS_ONLN to determine the maximum number of processes to
 23## start
 24sub get_online_processors {
 25	open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |")
 26		or die "$0: ERROR: failed to execute getconf: $!";
 27	my $procs = <$fh>;
 28	close($fh);
 29
 30	if (!($procs =~ /^\d+$/)) {
 31		return 1;
 32	}
 33
 34	return int($procs);
 35}
 36
 37## writes results to the parent process
 38## format: <file index> <initcall level> <base initcall section name>
 39sub write_results {
 40	my ($index, $initcalls) = @_;
 41
 42	# sort by the counter value to ensure the order of initcalls within
 43	# each object file is correct
 44	foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) {
 45		my $level = $initcalls->{$counter}->{'level'};
 46
 47		# section name for the initcall function
 48		my $secname = $initcalls->{$counter}->{'module'} . '__' .
 49			      $counter . '_' .
 50			      $initcalls->{$counter}->{'line'} . '_' .
 51			      $initcalls->{$counter}->{'function'};
 52
 53		print "$index $level $secname\n";
 54	}
 55}
 56
 57## reads a result line from a child process and adds it to the $results array
 58sub read_results{
 59	my ($fh) = @_;
 60
 61	# each child prints out a full line w/ autoflush and exits after the
 62	# last line, so even if buffered I/O blocks here, it shouldn't block
 63	# very long
 64	my $data = <$fh>;
 65
 66	if (!defined($data)) {
 67		return 0;
 68	}
 69
 70	chomp($data);
 71
 72	my ($index, $level, $secname) = $data =~
 73		/^(\d+)\ ([^\ ]+)\ (.*)$/;
 74
 75	if (!defined($index) ||
 76		!defined($level) ||
 77		!defined($secname)) {
 78		die "$0: ERROR: child process returned invalid data: $data\n";
 79	}
 80
 81	$index = int($index);
 82
 83	if (!exists($results->{$index})) {
 84		$results->{$index} = [];
 85	}
 86
 87	push (@{$results->{$index}}, {
 88		'level'   => $level,
 89		'secname' => $secname
 90	});
 91
 92	return 1;
 93}
 94
 95## finds initcalls from an object file or all object files in an archive, and
 96## writes results back to the parent process
 97sub find_initcalls {
 98	my ($index, $file) = @_;
 99
100	die "$0: ERROR: file $file doesn't exist?" if (! -f $file);
101
102	open(my $fh, "\"$nm\" --defined-only \"$file\" 2>/dev/null |")
103		or die "$0: ERROR: failed to execute \"$nm\": $!";
104
105	my $initcalls = {};
106
107	while (<$fh>) {
108		chomp;
109
110		# check for the start of a new object file (if processing an
111		# archive)
112		my ($path)= $_ =~ /^(.+)\:$/;
113
114		if (defined($path)) {
115			write_results($index, $initcalls);
116			$initcalls = {};
117			next;
118		}
119
120		# look for an initcall
121		my ($module, $counter, $line, $symbol) = $_ =~
122			/[a-z]\s+__initcall__(\S*)__(\d+)_(\d+)_(.*)$/;
123
124		if (!defined($module)) {
125			$module = ''
126		}
127
128		if (!defined($counter) ||
129			!defined($line) ||
130			!defined($symbol)) {
131			next;
132		}
133
134		# parse initcall level
135		my ($function, $level) = $symbol =~
136			/^(.*)((early|rootfs|con|[0-9])s?)$/;
137
138		die "$0: ERROR: invalid initcall name $symbol in $file($path)"
139			if (!defined($function) || !defined($level));
140
141		$initcalls->{$counter} = {
142			'module'   => $module,
143			'line'     => $line,
144			'function' => $function,
145			'level'    => $level,
146		};
147	}
148
149	close($fh);
150	write_results($index, $initcalls);
151}
152
153## waits for any child process to complete, reads the results, and adds them to
154## the $results array for later processing
155sub wait_for_results {
156	my ($select) = @_;
157
158	my $pid = 0;
159	do {
160		# unblock children that may have a full write buffer
161		foreach my $fh ($select->can_read(0)) {
162			read_results($fh);
163		}
164
165		# check for children that have exited, read the remaining data
166		# from them, and clean up
167		$pid = waitpid(-1, WNOHANG);
168		if ($pid > 0) {
169			if (!exists($jobs->{$pid})) {
170				next;
171			}
172
173			my $fh = $jobs->{$pid};
174			$select->remove($fh);
175
176			while (read_results($fh)) {
177				# until eof
178			}
179
180			close($fh);
181			delete($jobs->{$pid});
182		}
183	} while ($pid > 0);
184}
185
186## forks a child to process each file passed in the command line and collects
187## the results
188sub process_files {
189	my $index = 0;
190	my $njobs = $ENV{'PARALLELISM'} || get_online_processors();
191	my $select = IO::Select->new();
192
193	while (my $file = shift(@ARGV)) {
194		# fork a child process and read it's stdout
195		my $pid = open(my $fh, '-|');
196
197		if (!defined($pid)) {
198			die "$0: ERROR: failed to fork: $!";
199		} elsif ($pid) {
200			# save the child process pid and the file handle
201			$select->add($fh);
202			$jobs->{$pid} = $fh;
203		} else {
204			# in the child process
205			STDOUT->autoflush(1);
206			find_initcalls($index, "$objtree/$file");
207			exit;
208		}
209
210		$index++;
211
212		# limit the number of children to $njobs
213		if (scalar(keys(%{$jobs})) >= $njobs) {
214			wait_for_results($select);
215		}
216	}
217
218	# wait for the remaining children to complete
219	while (scalar(keys(%{$jobs})) > 0) {
220		wait_for_results($select);
221	}
222}
223
224sub generate_initcall_lds() {
225	process_files();
226
227	my $sections = {};	# level -> [ secname, ...]
228
229	# sort results to retain link order and split to sections per
230	# initcall level
231	foreach my $index (sort { $a <=> $b } keys(%{$results})) {
232		foreach my $result (@{$results->{$index}}) {
233			my $level = $result->{'level'};
234
235			if (!exists($sections->{$level})) {
236				$sections->{$level} = [];
237			}
238
239			push(@{$sections->{$level}}, $result->{'secname'});
240		}
241	}
242
243	die "$0: ERROR: no initcalls?" if (!keys(%{$sections}));
244
245	# print out a linker script that defines the order of initcalls for
246	# each level
247	print "SECTIONS {\n";
248
249	foreach my $level (sort(keys(%{$sections}))) {
250		my $section;
251
252		if ($level eq 'con') {
253			$section = '.con_initcall.init';
254		} else {
255			$section = ".initcall${level}.init";
256		}
257
258		print "\t${section} : {\n";
259
260		foreach my $secname (@{$sections->{$level}}) {
261			print "\t\t*(${section}..${secname}) ;\n";
262		}
263
264		print "\t}\n";
265	}
266
267	print "}\n";
268}
269
270generate_initcall_lds();