Modify / adapt C program from Linux to HP-UX

Hi

I've got what I needed from an EE expert for Linux but what I really need
more is for our HP-UX, ie the Linux cpulimit equivalent for HP-UX (Ver B11.23
running on PA-RISC hardware) :

  http://www.experts-exchange.com/OS/Unix/Q_25722390.html#a29889657


I got from sourceforge the codes for cpulimit.c but unlikely it will make/compile
on HP-UX.

Need someone to modify, make & test  the cpulimit.c source codes below to adapt to
 HP-UX:


=====================================================================

 * Author:  Angelo Marletta
 * Date:    26/06/2005
 * Version: 1.1
 * Last version at: http://marlon80.interfree.it/cpulimit/index.html
 *
 * Changelog:
 *  - Fixed a segmentation fault if controlled process exited in particular circumstances
 *  - Better CPU usage estimate
 *  - Fixed a <0 %CPU usage reporting in rare cases
 *  - Replaced MAX_PATH_SIZE with PATH_MAX already defined in <limits.h>
 *  - Command line arguments now available
 *  - Now is possible to specify target process by pid
 */


#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/resource.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>

//kernel time resolution (inverse of one jiffy interval) in Hertz
//i don't know how to detect it, then define to the default (not very clean!)
#define HZ 100

//some useful macro
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)

//pid of the controlled process
int pid=0;
//executable file name
char *program_name;
//verbose mode
int verbose=0;
//lazy mode
int lazy=0;

//reverse byte search
void *memrchr(const void *s, int c, size_t n);

//return ta-tb in microseconds (no overflow checks!)
inline long timediff(const struct timespec *ta,const struct timespec *tb) {
    unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000);
    return us;
}

int waitforpid(int pid) {
      //switch to low priority
      if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
            printf("Warning: cannot renice\n");
      }

      int i=0;

      while(1) {

            DIR *dip;
            struct dirent *dit;

            //open a directory stream to /proc directory
            if ((dip = opendir("/proc")) == NULL) {
                  perror("opendir");
                  return -1;
            }

            //read in from /proc and seek for process dirs
            while ((dit = readdir(dip)) != NULL) {
                  //get pid
                  if (pid==atoi(dit->d_name)) {
                        //pid detected
                        if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
                              //process is ok!
                              goto done;
                        }
                        else {
                              fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
                        }
                  }
            }

            //close the dir stream and check for errors
            if (closedir(dip) == -1) {
                  perror("closedir");
                  return -1;
            }

            //no suitable target found
            if (i++==0) {
                  if (lazy) {
                        fprintf(stderr,"No process found\n");
                        exit(2);
                  }
                  else {
                        printf("Warning: no target process found. Waiting for it...\n");
                  }
            }

            //sleep for a while
            sleep(2);
      }

done:
      printf("Process %d detected\n",pid);
      //now set high priority, if possible
      if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
            printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
      }
      return 0;

}

//this function periodically scans process list and looks for executable path names
//it should be executed in a low priority context, since precise timing does not matter
//if a process is found then its pid is returned
//process: the name of the wanted process, can be an absolute path name to the executable file
//         or simply its name
//return: pid of the found process
int getpidof(const char *process) {

      //set low priority
      if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
            printf("Warning: cannot renice\n");
      }

      char exelink[20];
      char exepath[PATH_MAX+1];
      int pid=0;
      int i=0;

      while(1) {

            DIR *dip;
            struct dirent *dit;

            //open a directory stream to /proc directory
            if ((dip = opendir("/proc")) == NULL) {
                  perror("opendir");
                  return -1;
            }

            //read in from /proc and seek for process dirs
            while ((dit = readdir(dip)) != NULL) {
                  //get pid
                  pid=atoi(dit->d_name);
                  if (pid>0) {
                        sprintf(exelink,"/proc/%d/exe",pid);
                        int size=readlink(exelink,exepath,sizeof(exepath));
                        if (size>0) {
                              int found=0;
                              if (process[0]=='/' && strncmp(exepath,process,size)==0 && size==strlen(process)) {
                                    //process starts with / then it's an absolute path
                                    found=1;
                              }
                              else {
                                    //process is the name of the executable file
                                    if (strncmp(exepath+size-strlen(process),process,strlen(process))==0) {
                                          found=1;
                                    }
                              }
                              if (found==1) {
                                    if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
                                          //process is ok!
                                          goto done;
                                    }
                                    else {
                                          fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
                                    }
                              }
                        }
                  }
            }

            //close the dir stream and check for errors
            if (closedir(dip) == -1) {
                  perror("closedir");
                  return -1;
            }

            //no suitable target found
            if (i++==0) {
                  if (lazy) {
                        fprintf(stderr,"No process found\n");
                        exit(2);
                  }
                  else {
                        printf("Warning: no target process found. Waiting for it...\n");
                  }
            }

            //sleep for a while
            sleep(2);
      }

done:
      printf("Process %d detected\n",pid);
      //now set high priority, if possible
      if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
            printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
      }
      return pid;

}

//SIGINT and SIGTERM signal handler
void quit(int sig) {
      //let the process continue if it's stopped
      kill(pid,SIGCONT);
      printf("Exiting...\n");
      exit(0);
}

//get jiffies count from /proc filesystem
int getjiffies(int pid) {
      static char stat[20];
      static char buffer[1024];
      sprintf(stat,"/proc/%d/stat",pid);
      FILE *f=fopen(stat,"r");
      if (f==NULL) return -1;
      fgets(buffer,sizeof(buffer),f);
      fclose(f);
      char *p=buffer;
      p=memchr(p+1,')',sizeof(buffer)-(p-buffer));
      int sp=12;
      while (sp--)
            p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
      //user mode jiffies
      int utime=atoi(p+1);
      p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
      //kernel mode jiffies
      int ktime=atoi(p+1);
      return utime+ktime;
}

//process instant photo
struct process_screenshot {
      struct timespec when;      //timestamp
      int jiffies;      //jiffies count of the process
      int cputime;      //microseconds of work from previous screenshot to current
};

//extracted process statistics
struct cpu_usage {
      float pcpu;
      float workingrate;
};

//this function is an autonomous dynamic system
//it works with static variables (state variables of the system), that keep memory of recent past
//its aim is to estimate the cpu usage of the process
//to work properly it should be called in a fixed periodic way
//perhaps i will put it in a separate thread...
int compute_cpu_usage(int pid,int last_working_quantum,struct cpu_usage *pusage) {
      #define MEM_ORDER 10
      //circular buffer containing last MEM_ORDER process screenshots
      static struct process_screenshot ps[MEM_ORDER];
      //the last screenshot recorded in the buffer
      static int front=-1;
      //the oldest screenshot recorded in the buffer
      static int tail=0;

      if (pusage==NULL) {
            //reinit static variables
            front=-1;
            tail=0;
            return 0;
      }

      //let's advance front index and save the screenshot
      front=(front+1)%MEM_ORDER;
      int j=getjiffies(pid);
      if (j>=0) ps[front].jiffies=j;
      else return -1;      //error: pid does not exist
      clock_gettime(CLOCK_REALTIME,&(ps[front].when));
      ps[front].cputime=last_working_quantum;

      //buffer actual size is: (front-tail+MEM_ORDER)%MEM_ORDER+1
      int size=(front-tail+MEM_ORDER)%MEM_ORDER+1;

      if (size==1) {
            //not enough samples taken (it's the first one!), return -1
            pusage->pcpu=-1;
            pusage->workingrate=1;
            return 0;
      }
      else {
            //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds
            long dt=timediff(&(ps[front].when),&(ps[tail].when));
            long dtwork=0;
            int i=(tail+1)%MEM_ORDER;
            int max=(front+1)%MEM_ORDER;
            do {
                  dtwork+=ps[i].cputime;
                  i=(i+1)%MEM_ORDER;
            } while (i!=max);
            int used=ps[front].jiffies-ps[tail].jiffies;
            float usage=(used*1000000.0/HZ)/dtwork;
            pusage->workingrate=1.0*dtwork/dt;
            pusage->pcpu=usage*pusage->workingrate;
            if (size==MEM_ORDER)
                  tail=(tail+1)%MEM_ORDER;
            return 0;
      }
      #undef MEM_ORDER
}

void print_caption() {
      printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n");
}

void print_usage(FILE *stream,int exit_code) {
      fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name);
      fprintf(stream, "   TARGET must be exactly one of these:\n");
      fprintf(stream, "      -p, --pid=N        pid of the process\n");
      fprintf(stream, "      -e, --exe=FILE     name of the executable program file\n");
      fprintf(stream, "      -P, --path=PATH    absolute path name of the executable program file\n");
      fprintf(stream, "   OPTIONS\n");
      fprintf(stream, "      -l, --limit=N      percentage of cpu allowed from 0 to 100 (mandatory)\n");
      fprintf(stream, "      -v, --verbose      show control statistics\n");
      fprintf(stream, "      -z, --lazy         exit if there is no suitable target process, or if it dies\n");
      fprintf(stream, "      -h, --help         display this help and exit\n");
      exit(exit_code);
}

int main(int argc, char **argv) {

      //get program name
      char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0]));
      program_name = p==NULL?argv[0]:(p+1);
      //parse arguments
      int next_option;
      /* A string listing valid short options letters. */
      const char* short_options="p:e:P:l:vzh";
      /* An array describing valid long options. */
      const struct option long_options[] = {
            { "pid", 0, NULL, 'p' },
            { "exe", 1, NULL, 'e' },
            { "path", 0, NULL, 'P' },
            { "limit", 0, NULL, 'l' },
            { "verbose", 0, NULL, 'v' },
            { "lazy", 0, NULL, 'z' },
            { "help", 0, NULL, 'h' },
            { NULL, 0, NULL, 0 }
      };
      //argument variables
      const char *exe=NULL;
      const char *path=NULL;
      int perclimit=0;
      int pid_ok=0;
      int process_ok=0;
      int limit_ok=0;

      do {
            next_option = getopt_long (argc, argv, short_options,long_options, NULL);
            switch(next_option) {
                  case 'p':
                        pid=atoi(optarg);
                        pid_ok=1;
                        break;
                  case 'e':
                        exe=optarg;
                        process_ok=1;
                        break;
                  case 'P':
                        path=optarg;
                        process_ok=1;
                        break;
                  case 'l':
                        perclimit=atoi(optarg);
                        limit_ok=1;
                        break;
                  case 'v':
                        verbose=1;
                        break;
                  case 'z':
                        lazy=1;
                        break;
                  case 'h':
                        print_usage (stdout, 1);
                        break;
                  case '?':
                        print_usage (stderr, 1);
                        break;
                  case -1:
                        break;
                  default:
                        abort();
            }
      } while(next_option != -1);

      if (!process_ok && !pid_ok) {
            fprintf(stderr,"Error: You must specify a target process\n");
            print_usage (stderr, 1);
            exit(1);
      }
      if ((exe!=NULL && path!=NULL) || (pid_ok && (exe!=NULL || path!=NULL))) {
            fprintf(stderr,"Error: You must specify exactly one target process\n");
            print_usage (stderr, 1);
            exit(1);
      }
      if (!limit_ok) {
            fprintf(stderr,"Error: You must specify a cpu limit\n");
            print_usage (stderr, 1);
            exit(1);
      }
      float limit=perclimit/100.0;
      if (limit<0 || limit >1) {
            fprintf(stderr,"Error: limit must be in the range 0-100\n");
            print_usage (stderr, 1);
            exit(1);
      }
      //parameters are all ok!
      signal(SIGINT,quit);
      signal(SIGTERM,quit);

      //time quantum in microseconds. it's splitted in a working period and a sleeping one
      int period=100000;
      struct timespec twork,tsleep;   //working and sleeping intervals
      memset(&twork,0,sizeof(struct timespec));
      memset(&tsleep,0,sizeof(struct timespec));

wait_for_process:

      //look for the target process..or wait for it
      if (exe!=NULL)
            pid=getpidof(exe);
      else if (path!=NULL)
            pid=getpidof(path);
      else {
            waitforpid(pid);
      }
      //process detected...let's play

      //init compute_cpu_usage internal stuff
      compute_cpu_usage(0,0,NULL);
      //main loop counter
      int i=0;

      struct timespec startwork,endwork;
      long workingtime=0;            //last working time in microseconds

      if (verbose) print_caption();

      float pcpu_avg=0;

      //here we should already have high priority, for time precision
      while(1) {

            //estimate how much the controlled process is using the cpu in its working interval
            struct cpu_usage cu;
            if (compute_cpu_usage(pid,workingtime,&cu)==-1) {
                  fprintf(stderr,"Process %d dead!\n",pid);
                  if (lazy) exit(2);
                  //wait until our process appears
                  goto wait_for_process;            
            }

            //cpu actual usage of process (range 0-1)
            float pcpu=cu.pcpu;
            //rate at which we are keeping active the process (range 0-1)
            float workingrate=cu.workingrate;

            //adjust work and sleep time slices
            if (pcpu>0) {
                  twork.tv_nsec=min(period*limit*1000/pcpu*workingrate,period*1000);
            }
            else if (pcpu==0) {
                  twork.tv_nsec=period*1000;
            }
            else if (pcpu==-1) {
                  //not yet a valid idea of cpu usage
                  pcpu=limit;
                  workingrate=limit;
                  twork.tv_nsec=min(period*limit*1000,period*1000);
            }
            tsleep.tv_nsec=period*1000-twork.tv_nsec;

            //update average usage
            pcpu_avg=(pcpu_avg*i+pcpu)/(i+1);

            if (verbose && i%10==0 && i>0) {
                  printf("%0.2f%%\t%6ld us\t%6ld us\t%0.2f%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100);
            }

            if (limit<1 && limit>0) {
                  //resume process
                  if (kill(pid,SIGCONT)!=0) {
                        fprintf(stderr,"Process %d dead!\n",pid);
                        if (lazy) exit(2);
                        //wait until our process appears
                        goto wait_for_process;
                  }
            }

            clock_gettime(CLOCK_REALTIME,&startwork);
            nanosleep(&twork,NULL);            //now process is working      
            clock_gettime(CLOCK_REALTIME,&endwork);
            workingtime=timediff(&endwork,&startwork);

            if (limit<1) {
                  //stop process, it has worked enough
                  if (kill(pid,SIGSTOP)!=0) {
                        fprintf(stderr,"Process %d dead!\n",pid);
                        if (lazy) exit(2);
                        //wait until our process appears
                        goto wait_for_process;
                  }
                  nanosleep(&tsleep,NULL);      //now process is sleeping
            }
            i++;
      }

}
sunhuxAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

yuzhCommented:
FYI, HP-UX has  rtsched and rtprio can do the similar job, login to your HP-UX box and do:
man rtsched
man rtprio
to learn more details.
also:
http://docs.hp.com/en/B2355-90690/rtsched.1.html 
http://docs.hp.com/en/B3921-90010/rtprio.1.html 
PS: since Linux and HP-UX run on different hardware platform, you will have lots of trouble to port it to HP-UX.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
sunhuxAuthor Commented:

Looks like "nice" command to me which will still allow the CPU hungry process
to chew up the processor resource that is not being used by other processes,
so I'll still be getting alerts that the CPU utilization is high.

Technically your solution or "nice" would enable other more critical processes
to run first (on higher priority) & only when there's free CPU, it will allow the
"wild" process to run, thus this is optimizing the usage of CPU, but I still can't
stop the monitoring alerts from coming to me (a secondary issue)
0
yuzhCommented:
They are better than "nice", "rtprio" or "rtsched" to control your process in real-time,
please have a look at :http:Q_21239985.htmland http:Q_20666340.html
0
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

gheistCommented:
0
sunhuxAuthor Commented:

Thanks, I heard abt WLM & PRM that would achieve what's needed; just that they don't
come free.  Have to be purchased
0
sunhuxAuthor Commented:

Not what's wanted but some other tools (chargeable
ones) were recommended
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.