# This file is part of ltrace.
# Copyright (C) 2014 Petr Machata, Red Hat Inc.
# Copyright (C) 2006 Yao Qi <qiyao@cn.ibm.com>, IBM Corporation
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA

# Objectives: Verify that Ltrace can trace all the system calls in
# execution.  Note that this test is necessarily noisy.  Dynamic
# linker adds a bunch of system calls of its own.

set empty [ltraceCompile {} [ltraceSource c {
    int main (void) { return 0; }
}]]

set bin [ltraceCompile {} [ltraceSource c {
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/syscall.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <stdlib.h>

    int
    main ()
    {
      FILE* fp;
      char s[]="system_calls";
      char buffer[1024];
      struct stat state;

      fp = fopen ("system_calls.tmp", "w");
      if (fp == NULL)
	{
	  printf("Can not create system_calls.tmp\n");
	  exit (0);
	}
      fwrite(s, sizeof(s), 1, fp);
      fseek (fp, 0, SEEK_CUR);
      fread(buffer, sizeof(s), 1, fp);
      fclose(fp);

      getcwd (buffer, sizeof buffer);
      chdir (".");
      symlink ("system_calls.tmp", "system_calls.link");
      remove("system_calls.link");
      rename ("system_calls.tmp", "system_calls.tmp1");
      stat ("system_calls.tmp", &state);
      access ("system_calls.tmp", R_OK);
      remove("system_calls.tmp1");

      mkdir ("system_call_mkdir", 0777);
      rmdir ("system_call_mkdir");

      return 0;
    }
}]]

proc Calls {logfile} {
    set fp [open $logfile]
    set ret {}

    while {[gets $fp line] >= 0} {
	if [regexp -- {^[a-zA-Z0-9]*@SYS} $line] {
	    set call [lindex [split $line @] 0]
	    dict incr ret $call
	}
    }

    close $fp
    return $ret
}

proc GetDefault {d key def} {
    if {[dict exists $d $key]} {
	return [dict get $d $key]
    } else {
	return $def
    }
}

proc Diff {d1 d2} {
    set keys [lsort -unique [concat [dict keys $d1] [dict keys $d2]]]
    set ret {}
    foreach key $keys {
	set n1 [GetDefault $d1 $key 0]
	set n2 [GetDefault $d2 $key 0]
	set sum [expr $n1 - $n2]
	if {[expr $sum != 0]} {
		dict set ret $key $sum
	}
    }
    return $ret
}

proc Match {d patterns} {
    foreach line $patterns {
	set pattern [lindex $line 0]
	set op [lindex $line 1]
	set expect [lindex $line 2]

	set count 0
	foreach key [dict keys $d] {
	    if [regexp -- $pattern $key] {
		incr count [dict get $d $key]
	    }
	}

	set msgMain "$pattern was recorded $count times"

	if {[eval expr $count $op $expect]} {
	    pass $msgMain
	} else {
	    fail "$msgMain, expected $op $expect"
	}
    }
}

Match [Diff [Calls [ltraceRun -L -S -- $bin]] \
	   [Calls [ltraceRun -L -S -- $empty]]] {
    { {^write$} == 1 }
    { {^unlink(at)?$} >= 2 }
    { {^open(at)?$} == 1 }
    { {^(new|f)?stat(64)?$} == 1 }
    { {^close$} == 1 }
    { {^getcwd$} == 1 }
    { {^chdir$} == 1 }
    { {^symlink(at)?$} == 1 }
    { {^f?access(at)?$} == 1 }
    { {^rename(at)?$} == 1 }
    { {^mkdir(at)?$} == 1 }
}

ltraceDone