cpulimit-win.c 7.03 KB
Newer Older
Francois Bobot's avatar
Francois Bobot committed
1 2
/**************************************************************************/
/*                                                                        */
MARCHE Claude's avatar
MARCHE Claude committed
3 4 5
/*  Copyright (C) 2008-2010                                               */
/*    Dillon PARIENTE 2008                                                */
/*    Claude MARCHE 2010                                                  */
Francois Bobot's avatar
Francois Bobot committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*                                                                        */
/*  This software is free software; you can redistribute it and/or        */
/*  modify it under the terms of the GNU Library General Public           */
/*  License version 2, with the special exception on linking              */
/*  described in file LICENSE.                                            */
/*                                                                        */
/*  This software 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.                  */
/*                                                                        */
/**************************************************************************/

/* $Id: cpulimit-win.c,v 1.3 2009-12-09 08:28:00 nrousset Exp $ */

20 21
#ifdef _WIN32

Francois Bobot's avatar
Francois Bobot committed
22
#include <windows.h>
23
#include <stdlib.h>
Francois Bobot's avatar
Francois Bobot committed
24 25
#include <stdio.h>
#include <errno.h>
26
#include <string.h>
Francois Bobot's avatar
Francois Bobot committed
27

28 29 30
static void
ErrorReport(char *function)
{
31
    char *message;
32 33 34 35 36 37 38 39 40 41 42
    DWORD error = GetLastError();

    FormatMessage(
                  FORMAT_MESSAGE_ALLOCATE_BUFFER |
                  FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL,
                  error,
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  (LPTSTR) &message,
                  0, NULL );

43
    printf("Fatal: %s failed with error %ld: %s",
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
           function, error, message);
    LocalFree(message);
}

static PROCESS_INFORMATION pi;
static HANDLE ghJob;

void terminates(void) {
  TerminateProcess(pi.hProcess, 10);
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
  CloseHandle(ghJob);
}
BOOL WINAPI ConsoleHandler(DWORD CEvent)
{
    /*    switch(CEvent)
    {
    case CTRL_C_EVENT:
    case CTRL_BREAK_EVENT:
    case CTRL_CLOSE_EVENT:
    case CTRL_LOGOFF_EVENT:
    case CTRL_SHUTDOWN_EVENT:*/
  printf("Got signal from console: killing subprocess\n");
  fflush(stdout);
  terminates();
  return TRUE;
}

Francois Bobot's avatar
Francois Bobot committed
72 73
int main(int argc, char *argv[]) {
  STARTUPINFO si;
74
  FILETIME ft_start, ft_stop, ft_system, ft_user;
75
  ULARGE_INTEGER ull_start, ull_stop, ull_system, ull_user;
76
  double cpu_time, wall_time;
77
  int i,showtime,hidetime;
Francois Bobot's avatar
Francois Bobot committed
78 79 80
  unsigned ex;
  unsigned long s = 0; // length of args after concat
  char * p; // command line parameter
81 82 83 84 85
  long long time_limit_seconds=0,memory_limit_MiB=0;
  unsigned error = 0;
  JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits;
  ghJob = CreateJobObject(NULL,NULL);
  if(!ghJob) ErrorReport("CreateJobObject");
Francois Bobot's avatar
Francois Bobot committed
86 87 88
  ZeroMemory(&si, sizeof(si));
  si.cb = sizeof(si);
  ZeroMemory(&pi, sizeof(pi));
89
  SetConsoleCtrlHandler(&ConsoleHandler,TRUE);
90

91 92 93 94 95 96 97 98 99
  if (argc<5) error=1;
  else {
    time_limit_seconds = strtol (argv[1], &p, 10);
    if (*p!='\0') error=1;
    memory_limit_MiB = strtol (argv[2], &p, 10);
    if (*p!='\0') error=1;
    showtime = !strncmp("-s",argv[3],3);
    hidetime = !strncmp("-h",argv[3],3);
  }
100

101
  if (error || !(showtime || hidetime)) {
102 103 104 105
    fprintf(stderr, "usage: %s <time limit in seconds> <virtual memory limit in MiB>\n"
                    "          <show/hide cpu time: -s|-h> <command> <args>...\n\n"
                    "Zero sets no limit (keeps the actual limit)\n", argv[0]);
    return EXIT_FAILURE;
Francois Bobot's avatar
Francois Bobot committed
106
  }
107
  // concats argv[4..] into a single command line parameter and puts quotes around arguments
108
  for (i = 4; i < argc; i++)
109
    s += strlen(argv[i]) + 3;
Francois Bobot's avatar
Francois Bobot committed
110
  // CreateProcess does not allow more than 32767 bytes for command line parameter
111
  if (s > 32767) {
Francois Bobot's avatar
Francois Bobot committed
112
    printf("%s: Error: parameter's length exceeds CreateProcess limits\n", argv[0]);
113
    return EXIT_FAILURE;
Francois Bobot's avatar
Francois Bobot committed
114 115 116 117
  }
  p = (char*) malloc(s);
  if (p == NULL) {
    printf("%s: Error: when allocating %d bytes in memory\n", argv[0], (int) s);
118
    return EXIT_FAILURE;
Francois Bobot's avatar
Francois Bobot committed
119
  }
MARCHE Claude's avatar
MARCHE Claude committed
120
  *p = '\0';
121
  for (i = 4; i < argc; i++) {
122 123 124
    strcat(p, "\"");
    strcat(p, argv[i]);
    strcat(p, "\"");
Francois Bobot's avatar
Francois Bobot committed
125 126
    if (i < argc - 1) strcat(p, " ");
  }
127

128 129 130 131
  if (time_limit_seconds!=0||memory_limit_MiB!=0)
    {/* Set the time limit */
    ULONGLONG timeout;
    ZeroMemory(&limits, sizeof(limits));
132
    limits.BasicLimitInformation.LimitFlags =
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
      ((time_limit_seconds==0)?0:JOB_OBJECT_LIMIT_PROCESS_TIME)
      |((memory_limit_MiB==0)?0:JOB_OBJECT_LIMIT_PROCESS_MEMORY);

    // seconds to W32 kernel ticks
    if (time_limit_seconds!=0) {
      timeout = 1000ULL * 1000ULL * 10ULL * time_limit_seconds;
      limits.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart=timeout;
    }
    if (memory_limit_MiB!=0) {
      size_t memory = 1024 * 1024 * memory_limit_MiB;
      limits.ProcessMemoryLimit = memory;
    }

    if (!SetInformationJobObject(ghJob, JobObjectExtendedLimitInformation,
				 &limits, sizeof(limits))) {
      ErrorReport("SetInformationJobObject");
      return EXIT_FAILURE;
    }
    }

Francois Bobot's avatar
Francois Bobot committed
153
  // launches "child" process with command line parameter
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  if(!CreateProcess(NULL,p,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi))
    {
      printf( "%s: Error: failed when launching <%s>\n", argv[0], p);
      ErrorReport("CreateProcess");
      return EXIT_FAILURE;
    }
  if(!AssignProcessToJobObject(ghJob,pi.hProcess))
    {ErrorReport("AssignProcessToJobObject");
      return EXIT_FAILURE;
    }

  /* Let's resume the process */
  ResumeThread(pi.hThread);
  // waits and frees handles and malloc
  int w;
    switch (w=WaitForSingleObject(pi.hProcess, INFINITE))
    {
171
    case WAIT_FAILED:
172 173 174 175 176 177 178 179 180
      printf("Wait failed"); break;
    case WAIT_TIMEOUT:
      printf("Wait timeout"); break;
    case WAIT_OBJECT_0:
      /* Normal case others should not happen. */ break;
    case WAIT_ABANDONED:
      printf("Wait abandonned"); break;
    };

Francois Bobot's avatar
Francois Bobot committed
181
  GetExitCodeProcess(pi.hProcess, (LPDWORD) &ex);
182

183 184
  if (showtime) {
    GetProcessTimes(pi.hProcess, &ft_start, &ft_stop, &ft_system, &ft_user);
185 186 187 188 189 190 191 192
    ull_start.LowPart = ft_start.dwLowDateTime;
    ull_start.HighPart = ft_start.dwHighDateTime;
    ull_stop.LowPart = ft_stop.dwLowDateTime;
    ull_stop.HighPart = ft_stop.dwHighDateTime;
    ull_system.LowPart = ft_system.dwLowDateTime;
    ull_system.HighPart = ft_system.dwHighDateTime;
    ull_user.LowPart = ft_user.dwLowDateTime;
    ull_user.HighPart = ft_user.dwHighDateTime;
193
    cpu_time =
194 195
      ((ull_system.QuadPart + ull_user.QuadPart + 0.0) / 10000000.);
    wall_time = (ull_stop.QuadPart - ull_start.QuadPart + 0.0) / 10000000.;
196 197
    fprintf(stdout,
	    "why3cpulimit time : %f s\nwhy3cpulimit real time : %f s\n", cpu_time, wall_time);
198
    fflush(stdout);
199
  }
Francois Bobot's avatar
Francois Bobot committed
200 201
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
202
  CloseHandle(ghJob);
Francois Bobot's avatar
Francois Bobot committed
203 204 205 206 207
  free(p);
  if (w == WAIT_TIMEOUT) return 152;
  return ex;
}

208 209
#endif /* _WIN32 */

Francois Bobot's avatar
Francois Bobot committed
210
// How to compile under Cygwin (needs make, gcc and win32api):
211
//                 gcc -Wall -o cpulimit cpulimit.c