Saturday, September 27, 2014

Unix Prog: Process Accounting

1. Enable the process accounting
 ubuntu@ip-172-31-23-227:~$ accton --help  
 Usage: accton [OPTION] on|off|ACCOUNTING_FILE  
   
      Turns process accounting on or off, or changes the file where this  
      info is saved.  
   
      OPTIONS:  
      -h, --help    Show help and exit  
      -V, --version  Show version and exit  
   
      ARGUMENTS:  
      on        Activate process accounting and use default file  
      off       Deactivate process accounting  
      ACCOUNTING_FILE Activate (if not active) and save information in  
      this file  
   
      The system's default process accounting file is '/var/log/account/pacct'.  
   
      Report bugs to <bug-acct@gnu.org>  

shell:
 ubuntu@ip-172-31-23-227:~$ sudo accton ./own_acct  
 Turning on process accounting, file set to './own_acct'.  
 ubuntu@ip-172-31-23-227:~$ ls -lrt own_acct  
 -rw-rw-r-- 1 ubuntu ubuntu 128 Sep 27 19:27 own_acct  

2. Process Accounting Data Structure

 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/acct.h  
 ......  
 /*  
  comp_t is a 16-bit "floating" point number with a 3-bit base 8  
  exponent and a 13-bit fraction. See linux/kernel/acct.c for the  
  specific encoding system used.  
 */  
   
 typedef u_int16_t comp_t;  
   
 struct acct  
 {  
  char ac_flag;         /* Flags. */  
  u_int16_t ac_uid;       /* Real user ID. */  
  u_int16_t ac_gid;       /* Real group ID. */  
  u_int16_t ac_tty;       /* Controlling terminal. */  
  u_int32_t ac_btime;      /* Beginning time. */  
  comp_t ac_utime;       /* User time. */  
  comp_t ac_stime;       /* System time. */  
  comp_t ac_etime;       /* Elapsed time. */  
  comp_t ac_mem;        /* Average memory usage. */  
  comp_t ac_io;         /* Chars transferred. */  
  comp_t ac_rw;         /* Blocks read or written. */  
  comp_t ac_minflt;       /* Minor pagefaults. */  
  comp_t ac_majflt;       /* Major pagefaults. */  
  comp_t ac_swaps;       /* Number of swaps. */  
  u_int32_t ac_exitcode;    /* Process exitcode. */  
  char ac_comm[ACCT_COMM+1];  /* Command name. */  
  char ac_pad[10];       /* Padding bytes. */  
 };  
 ......  

Macro used to parse "ac_flag" field:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/x86_64-linux-gnu/sys/acct.h  
 ......  
 enum  
  {  
   AFORK = 0x01,        /* Has executed fork, but no exec. */  
   ASU = 0x02,         /* Used super-user privileges. */  
   ACORE = 0x08,        /* Dumped core. */  
   AXSIG = 0x10        /* Killed by a signal. */  
  };  
 ......  

Notes:
1) ac_btime: beginning time records when the process starts, but the time only end up with accuracy of second level, ac_etime: elapsed time records how many clock ticks the process uses. Based on ac_btime, ac_etime and ending order of process accounting record, we can't get the starting order of each process, if each process starts at the same second.

2) If Program A runs exec to launch program B which then run exec to launch program C, "A B C" happens at the same process, so only one process accounting record is written to describe 3 programs.

3. Example:

Process.c will create several processes, each process will be terminated in different ways.

And then we can use accounting.c to read the process accounting file

process.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
 #include<signal.h>  
   
 int main(int argc, char* argv[])  
 {  
  pid_t pid;  
   
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(1);  
  }  
  else if(pid != 0) { // parent process  
   sleep(2);  
   exit(2);  
  }  
   
  // First Child Process  
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(1);  
  }  
  else if(pid != 0) {  
   sleep(4);  
   abort();  
  }  
   
  // Second Child Process  
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(1);  
  }  
  else if(pid != 0) {  
   execl("/usr/bin/date", "date", (char*)0);  
   exit(7);  
  }  
   
  // Third Child Process  
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(1);  
  }  
  else if(pid != 0) {  
   sleep(8);  
   exit(0);  
  }  
   
  // Fourth Child Process  
  sleep(6);  
  kill(getpid(), SIGKILL); // the process will be terminated immediately  
 }  

accounting.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<sys/acct.h>  
 #include<unistd.h>  
   
 #ifdef HAS_SA_STAT  
 #define FMT "%s e = %6ld, chars = %7ld, stat = %3u: %c %c %c %c\n"  
 #else  
 #define FMT "%s e = %6ld, chars = %7ld, %c %c %c %c\n"  
 #endif  
 #ifndef HAS_ACORE  
 #define ACORE 0  
 #endif  
 #ifndef HAS_AXSIG  
 #define AXSIG 0  
 #endif  
   
 static unsigned long compt2ulong(comp_t comptime)  
 {  
  unsigned long val;  
  int exp;  
   
  val = comptime & 0x1fff; // val stores lower 13 bits of comptime  
  exp = (comptime >> 13) & 7; // exp stores lower position 16 15 14, 3 bits of value  
   
  while(exp-- > 0)  
   val *= 8;  
  return (val);  
 }  
   
 int main(int argc, char* argv[])  
 {  
  struct acct acdata;  
  FILE *fp;  
   
  if(argc != 2) {  
   printf("usage: pracct filename\n");  
   exit(1);  
  }  
   
  if((fp = fopen(argv[1], "r")) == NULL) {  
   printf("fopen error!\n");  
   exit(2);  
  }  
   
  // Read the accounting record, and output to shell  
  while(fread(&acdata, sizeof(acdata), 1, fp) == 1) {  
   printf(FMT, acdata.ac_comm, compt2ulong(acdata.ac_etime), compt2ulong(acdata.ac_io),  
 #ifdef HAS_SA_STAT  
       (unsigned char) acdata.ac_stat,  
 #endif  
       acdata.ac_flag & ACORE ? 'D' : ' ',  
       acdata.ac_flag & AXSIG ? 'X' : ' ',  
       acdata.ac_flag & AFORK ? 'F' : ' ',  
       acdata.ac_flag & ASU  ? 'S' : ' ');  
  }  
   
  if(ferror(fp)) {  
   printf("read error!\n");  
   exit(3);  
  }  
   
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ sudo accton ./own_acct  
 Turning on process accounting, file set to './own_acct'.  
 ubuntu@ip-172-31-23-227:~$ ./process.out  
 ubuntu@ip-172-31-23-227:~$ ./pracct.out ./own_acct  

No comments:

Post a Comment