blob: 22c2868e70558452f4d8ba0d9f58f62815ba9790 [file] [log] [blame]
#!/usr/bin/perl -w
use strict;
use warnings;
use Getopt::Long;
my $instructions = "
$0
Automatically generate some C code which runs various checks on a structure.
This fragment can then be placed on MuC, LHost, or target userspace programs
compiled with both gcc and mcc.
ALWAYS MANUALLY INSPECT THE OUTPUT. This tool just helps to avoid repetetive
grunt work when creating these test files, it will has bugs and corner cases.
Usage: $0 [--main] [--copts=\"-Iinclude/\"] header1 [header2...]
--main Generate a main function and includes so this
can be used as a standalone program.
--copts C options to be passed to the C preprocessor
whan parsing the headers
";
my $copts = "";
my $main = undef;
GetOptions(
"main" => \$main,
"copts=s" => \$copts,
);
my @headers = @ARGV;
if ($#headers < 0) {
die $instructions;
}
print make_main_header(\@headers) if $main;
print make_test_file(parse_headers());
print make_main_footer(\@headers) if $main;
sub parse_headers {
my %structure_fields;
my @structure_names;
foreach my $header (@headers) {
my $current_struct = undef;
foreach (`gcc -E -dD $header`) {
chomp;chomp;
if ((!defined($current_struct)) && /^struct\s+\b(\w+)\b\s*/ ) {
$current_struct = $1;
push(@structure_names, $current_struct);
#warn "Structure '$current_struct' from line $_\n";
}
elsif (defined($current_struct) && /\b(\w+)\b\s*(\[.*\]|)\s*\;/ && !(/\:/) ) {
push(@{$structure_fields{$current_struct}}, $1);
#warn "Field '$current_struct.$1' from line $_\n";
}
elsif ( /\}/ ) {
$current_struct = undef;
}
}
}
return (\%structure_fields, \@structure_names);
}
sub make_test_file {
my ($structure_fields, $structure_names) = @_;
my $output = "\n\n";
$output .= "/********************************************************************/\n";
$output .= "/********************************************************************/\n";
$output .= "/********************************************************************/\n";
$output .= "/********************************************************************/\n";
$output .= "/** Generated by $0 **/\n";
$output .= "
#if defined(MUC_BUILD)
# define printf PRINTF
# define sprintf uc_sprintf
#endif
";
$output .= c_utilities();
$output .= struct_check_func_header();
foreach my $structure_name (@$structure_names) {
$output .= struct_test($structure_name, $structure_fields->{$structure_name});
}
$output .= struct_check_func_footer();
$output .= "/** End generated by $0 **/\n";
$output .= "/********************************************************************/\n";
$output .= "/********************************************************************/\n";
$output .= "/********************************************************************/\n\n";
return $output;
}
sub check_line {
my $a = shift;
return "\thash = struct_check_func(hash, \"$a\", $a);\n";
}
sub struct_test {
my ($name, $fields_ref) = @_;
my $output = "";
$output .= check_line("sizeof(struct $name)");
foreach my $field (@$fields_ref) {
$output .= check_line("offsetof(struct $name, $field)");
}
$output .= "\n";
return $output;
}
sub c_utilities {
return "
static __inline__ unsigned long struct_check_hash(const unsigned long hash, const char* str)
{
unsigned long h = hash;
const char* p;
for (p = str; p && *p; p++)
h = ((h * 3571) + *p) * 1531;
return h;
}
static __inline__ unsigned long struct_check_func(const unsigned long hash, const char* check_str, unsigned long value)
{
char buf[128];
sprintf(buf, \"%s == %d\\n\", check_str, (int)value);
printf(\"%s\", buf);
return struct_check_hash(hash, buf);
}
";
}
sub struct_check_func_header {
return "
static __inline__ void struct_check_all(void)
{
unsigned long hash = 1;
";
}
sub struct_check_func_footer {
return "
printf(\"Final hash: %d\\n\", (int)hash);
}
";
}
sub make_main_header {
my $headers_ref = shift;
my $output = "
#include <stdio.h>
#include <stddef.h>
typedef unsigned int u32;
typedef signed int s32;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned char u8;
typedef signed char s8;
";
for my $header (@$headers_ref) {
$output .= "#include \"$header\"\n";
}
return $output;
}
sub make_main_footer {
return "
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
struct_check_all();
return 0;
}
";
}