84e9845897
[ROCm/clr commit: 71c6535b07]
247 строки
8.4 KiB
Perl
Исполняемый файл
247 строки
8.4 KiB
Perl
Исполняемый файл
#!/usr/bin/perl
|
|
# Copyright (c) 2020-2021 Advanced Micro Devices, Inc. All rights reserved.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
|
|
use strict;
|
|
use File::Copy;
|
|
use File::Spec;
|
|
use File::Basename;
|
|
use File::Which;
|
|
use Cwd 'realpath';
|
|
use Getopt::Std;
|
|
use List::Util qw(max);
|
|
use URI::Escape;
|
|
|
|
my $extract_range_specifier;
|
|
my $extract_pid;
|
|
my $extract_file;
|
|
my $output_file;
|
|
my $output_path;
|
|
my $extract_offset;
|
|
my $extract_size;
|
|
my $pid_running;
|
|
my $verbose=0;
|
|
my $error=0;
|
|
my $output_to_stdout=0;
|
|
|
|
sub usage {
|
|
print("Usage: $0 [-o|v|h] URI... \n");
|
|
print(" URIs can be read from STDIN, one per line.\n");
|
|
print(" From the URIs specified, extracts code objects into files named: ");
|
|
print("<executable_name>-[pid<number>]-offset<number>-size<number>.co\n\n");
|
|
print("Options:\n");
|
|
print(" -o <path> \tPath for output. If \"-\" specified, code object is printed to STDOUT.\n");
|
|
print(" -v \tVerbose output to STDOUT.\n");
|
|
print(" -h \tShow this help message.\n");
|
|
print("\nURI syntax:\n");
|
|
print("\tcode_object_uri ::== file_uri | memory_uri\n");
|
|
print("\tfile_uri ::== \"file://\" extract_file [ range_specifier ]\n");
|
|
print("\tmemory_uri ::== \"memory://\" process_id range_specifier\n");
|
|
print("\trange_specifier ::== range_delimiter range_attribute [\"&\" range_attribute]\n");
|
|
print("\trange_delimiter ::== \"#\" | \"?\"\n");
|
|
print("\trange_attribute ::== [\"offset=\" number | \"size=\" number ]\n");
|
|
print("\textract_file ::== URI_ENCODED_OS_FILE_PATH\n");
|
|
print("\tprocess_id ::== DECIMAL_NUMBER\n");
|
|
print("\tnumber ::== HEX_NUMBER \| DECIMAL_NUMBER \| OCTAL_NUMBER\n\n");
|
|
print("\tExample: file://dir1/dir2/hello_world#offset=133&size=14472 \n");
|
|
print("\t memory://1234#offset=0x20000&size=3000\n\n");
|
|
print(" NOTES:\n\n");
|
|
print("\tWhen specifying a URI in a shell command you will need to escape the \'&\' character in the range_specifier.\n");
|
|
print("\tIf \"size=\" is not specified, the default is the remainder of the file from the given offset.\n\n");
|
|
|
|
exit($error);
|
|
}
|
|
|
|
# Process options
|
|
my %options=();
|
|
getopts('vho:', \%options);
|
|
|
|
# this tool has been deprecated
|
|
print(STDERR "Warning: This tool has been DEPRECATED. Similar functionality is provided by llvm-objdump in the rocm-llvm package.\n");
|
|
|
|
if (defined $options{h}) {
|
|
usage();
|
|
}
|
|
|
|
if (defined $options{v}) {
|
|
$verbose = 1;
|
|
}
|
|
|
|
if (defined $options{o}) {
|
|
$output_path = $options{o};
|
|
if ($output_path eq "-") {
|
|
$output_to_stdout=1;
|
|
} else {
|
|
(-d $output_path) || die("Error: Path \'$output_path\' cannot be found.\n");
|
|
}
|
|
}
|
|
|
|
# Only push STDIN if there are no arguments -- otherwise this
|
|
# consumes the caller's stdin by accident.
|
|
# push STDIN to ARGV array.
|
|
if ($#ARGV < 0) {
|
|
push @ARGV, <STDIN> unless -t STDIN;
|
|
}
|
|
|
|
# error check: enough arguments presented.
|
|
if ($#ARGV < 0) {
|
|
print(STDERR "Error: No arguments.\n"); $error++;
|
|
usage();
|
|
}
|
|
|
|
# error check: command dd is available.
|
|
my $dd_cmd = which("dd");
|
|
(-f $dd_cmd) || die("Error: Can't find dd command\n");
|
|
|
|
foreach my $uri_str(@ARGV) {
|
|
chomp $uri_str;
|
|
|
|
my ($uri_protocol, $specs) = split(/:\/\//,$uri_str);
|
|
my $decoded_extract_file;
|
|
my $file_size;
|
|
|
|
if (lc($uri_protocol) eq "file") {
|
|
# expect file path
|
|
($extract_file, $extract_range_specifier) = split(/[#,?]/,$specs);
|
|
|
|
# decode the file name. URIs may have file/path names with non-alphanumeric characters, which will be encoded with %. We need to decode these.
|
|
$decoded_extract_file = uri_unescape($extract_file);
|
|
|
|
# verify file exists:
|
|
if (! -e $decoded_extract_file) {
|
|
print(STDERR "Error: can't find file: $decoded_extract_file\n"); $error++;
|
|
next;
|
|
}
|
|
|
|
# use the output_path is specified, otherwise use current working dir.
|
|
if ($output_path ne "") {
|
|
$output_file = File::Spec->catfile($output_path, basename($decoded_extract_file));
|
|
} else {
|
|
$output_file = basename($decoded_extract_file);
|
|
}
|
|
|
|
} elsif ( lc($uri_protocol) eq "memory") {
|
|
# expect memory specifier
|
|
($extract_pid, $extract_range_specifier) = split(/[#,?]/,$specs);
|
|
|
|
# verify pid is currently running
|
|
$pid_running = kill 0, $extract_pid;
|
|
if (! $pid_running) {
|
|
print(STDERR "Error: PID: $extract_pid is NOT running\n"); $error++;
|
|
next;
|
|
}
|
|
|
|
# get pid filename:
|
|
$extract_file = "/proc/$extract_pid/mem";
|
|
|
|
# verify file exists:
|
|
if (! -e $extract_file) {
|
|
print(STDERR "Error: can't find file: $extract_file\n"); $error++;
|
|
next;
|
|
}
|
|
|
|
# for extracting from a pid, make the output file in the current dir/path with the pid value as a name.
|
|
$output_file = "pid${extract_pid}";
|
|
|
|
# need to set $decoded_extract_file, because later we use this for other checks.
|
|
$decoded_extract_file = $extract_file;
|
|
} else {
|
|
# error, unrecognized Code Object URI
|
|
print(STDERR "Error: \'$uri_protocol\' is not recognized as a supported code object URI.\n"); $error++;
|
|
next;
|
|
}
|
|
|
|
# it is valid to not give a range specifier in a URI, in which case the entire code object will be extracted.
|
|
if ($extract_range_specifier ne "") {
|
|
my @tokens;
|
|
my $str;
|
|
my $value;
|
|
my $size_specified = 0;
|
|
|
|
@tokens = split(/[&]/,$extract_range_specifier);
|
|
foreach (@tokens) {
|
|
($str,$value) = split(/=/,$_);
|
|
if ($str eq "size") {
|
|
$extract_size=$value;
|
|
$size_specified = 1;
|
|
} elsif ($str eq "offset") {
|
|
$extract_offset=$value;
|
|
}
|
|
}
|
|
|
|
if ($size_specified != 1) {
|
|
# "size" not specified. default to rest of file (total size - offset)
|
|
$extract_size = -s $decoded_extract_file;
|
|
$extract_size -= $extract_offset;
|
|
}
|
|
|
|
} else {
|
|
# Error if URI is a memory request, and we have no range_specifier.
|
|
if ($pid_running) {
|
|
print(STDERR "Error: must specify a Range Specifier (offset and size) for a memory URI: $uri_str\n"); $error++;
|
|
next;
|
|
}
|
|
|
|
$extract_offset = 0;
|
|
$extract_size = -s $decoded_extract_file;
|
|
}
|
|
|
|
# We should have at least a valid size to extract; ignore cases with size=0.
|
|
if ($extract_size != 0) {
|
|
print("Reading input file \"$extract_file\" ...\n") if ($verbose);
|
|
|
|
# only if this is a File URI.
|
|
if (lc($uri_protocol) eq "file") {
|
|
# verify that offset+size does not exceed file size:
|
|
my $file_size = -s $decoded_extract_file;
|
|
my $size = int($extract_offset) + int($extract_size);
|
|
if ( $size > $file_size ) {
|
|
print(STDERR "Error: requested offset($extract_offset) + size($extract_size) exceeds file size($file_size) for file \"$decoded_extract_file\".\n"); $error++;
|
|
next;
|
|
}
|
|
}
|
|
|
|
open(INPUT_FP, "<", $decoded_extract_file) || die $!;
|
|
binmode INPUT_FP;
|
|
|
|
# extract the code object
|
|
my $co_filename;
|
|
if (!$output_to_stdout) {
|
|
$co_filename = "of=\'${output_file}-offset${extract_offset}-size${extract_size}.co\'";
|
|
}
|
|
|
|
my $dd_cmd_str = "$dd_cmd if=\'$decoded_extract_file\' $co_filename skip=$extract_offset count=$extract_size bs=1 status=none";
|
|
|
|
print("DD Command: $dd_cmd_str\n") if ($verbose);
|
|
|
|
my $dd_ret = system($dd_cmd_str);
|
|
if ($dd_ret != 0) {
|
|
print(STDERR "Error: DD command ($dd_cmd_str) failed with RC: $dd_ret\n"); $error++;
|
|
}
|
|
|
|
print("Extract request: file: $extract_file offset: $extract_offset size: $extract_size\n") if ($verbose);
|
|
} else {
|
|
print("Warning: trying to extract from $extract_file at offset=$extract_offset with size=0. Nothing to extract.\n") if ($verbose);
|
|
}
|
|
|
|
} # end of for each (URI) argument
|
|
|
|
exit($error);
|