Description:
box uses user time
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r130:db5312fad511 - - 1 file changed: 26 inserted, 4 deleted

@@ -1,428 +1,450
1 1 /*
2 2 This sandbox module is from [Fossil
3 3 grader](http://code.google.com/p/fossil-grader/).
4 4
5 5 This library is a modification from a program called trun, taken
6 6 from an unknown source. (FIX THIS)
7 7
8 8 When compiling with Mingw, add "-lpsapi" to link Windows'memory stat
9 9 library.
10 10 */
11 11 #include <windows.h>
12 12 #include <psapi.h>
13 13 #include <tlhelp32.h>
14 14 #include <stdio.h>
15 15 #include "execute.h"
16 16
17 17 #define INITIAL_WAIT_FOR_MEM_CHECK 100
18 18
19 19 /*
20 20 ==How execute works==
21 21
22 22 ===Start up===
23 23 Set up basic configurations: input file, output file
24 24 into STARTUPINFO struct to be passed to CreateProcess.
25 25
26 26 Create a child process with CreateProcess.
27 27
28 28 ===Wait===
29 29 Use WaitForSingleObject to wait.
30 30
31 31 ===Killing chile process===
32 32 This process is really involved, because (1) programs in
33 33 DOS mode actually runs inside NTVDM so killing them
34 34 requires to kill NTVDM, (2) something a program crashes
35 35 NTVDM and a dialog box pops up, and we need to close
36 36 that dialog box MANUALLY, and (3) for Win32 apps that crash,
37 37 some reporting service in Windows opens a dialog box,
38 38 and it has to be killed.
39 39
40 40 Those extra steps are what's exactly done here:
41 41 1. Kill the process if there's any
42 42 2. In case that there's no real process, find NTVDM
43 43 and kill it (repeatedly until it's gone)
44 44 3. Check if NTVDM crashed and some warning dialog opens,
45 45 if there's any, signal the user and wait.
46 46 4. For real Win32 apps, find process "dwwin.exe" which
47 47 represents an agent for reporting service and also
48 48 opens a dialog. If finds it, kill it (repeatedly)
49 49 until it's gone.
50 50
51 51 Step 4. might be problematic --- dwwin.exe might not
52 52 be a universal process for error reporting services???
53 53 */
54 54
55 55
56 56
57 57 /*
58 58 These are routines that check NTVDM crash dialog.
59 59 It works by enumerating all window titles, and
60 60 checks for "16 bit" or something with ".exe" somewhere
61 61 and starts with "cmd.exe".
62 62 */
63 63 bool NTVDMcrashed_found;
64 64
65 65 /* this is a callback for window title enumeration */
66 66 BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
67 67 {
68 68 char buffer[256];
69 69 GetWindowText(hWnd, buffer, 256);
70 70
71 71 if(strlen(buffer)!=0) {
72 72 if(strstr(buffer,"16 bit")!=0) {
73 73 NTVDMcrashed_found = true;
74 74 }
75 75 if((strstr(buffer,".exe")!=0) &&
76 76 (strstr(buffer,"cmd.exe")==buffer)) {
77 77 NTVDMcrashed_found = true;
78 78 printf("Title: %s\n",buffer);
79 79 }
80 80 }
81 81 return TRUE;
82 82 }
83 83
84 84 bool check_ntvdm_dialog()
85 85 {
86 86 NTVDMcrashed_found = false;
87 87
88 88 FARPROC EnumProcInstance = MakeProcInstance((FARPROC)EnumWindowsProc,
89 89 AfxGetInstanceHandle());
90 90 EnumWindows((WNDENUMPROC)EnumProcInstance, (LPARAM)0);
91 91 FreeProcInstance(EnumProcInstance);
92 92
93 93 return NTVDMcrashed_found;
94 94 }
95 95
96 96 DWORD get_process_id(char *pname)
97 97 {
98 98 HANDLE hProcessSnap;
99 99 HANDLE hProcess;
100 100 PROCESSENTRY32 pe32;
101 101 DWORD dwPriorityClass;
102 102 DWORD pid=0;
103 103
104 104 hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
105 105 if( hProcessSnap == INVALID_HANDLE_VALUE ) {
106 106 return 0;
107 107 }
108 108
109 109 pe32.dwSize = sizeof( PROCESSENTRY32 );
110 110 if( !Process32First( hProcessSnap, &pe32 ) ) {
111 111 CloseHandle( hProcessSnap );
112 112 return 0;
113 113 }
114 114
115 115 do {
116 116 if(strcasecmp(pe32.szExeFile ,pname)==0)
117 117 pid = pe32.th32ProcessID;
118 118 } while( Process32Next( hProcessSnap, &pe32 ) );
119 119
120 120 CloseHandle( hProcessSnap );
121 121 return pid;
122 122 }
123 123
124 124 DWORD get_ntvdm_pid()
125 125 {
126 126 return get_process_id("ntvdm.exe");
127 127 }
128 128
129 129 void kill_error_report()
130 130 {
131 131 DWORD pid;
132 132 do {
133 133 if((pid = get_process_id("dwwin.exe"))!=0) {
134 134 fprintf(stderr," -- with error report (pid: %ld)\n",pid);
135 135 HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid);
136 136 if(hProcess!=NULL) {
137 137 TerminateProcess(hProcess, 0);
138 138 Sleep(500);
139 139 while(get_process_id("dwwin.exe")==pid) {
140 140 fprintf(stderr,"wait for dwwin.exe to die...\n");
141 141 Sleep(500);
142 142 }
143 143 } else
144 144 fprintf(stderr,"do not have permission (%d)\n",
145 145 GetLastError());
146 146 }
147 147 } while(get_process_id("dwwin.exe")!=0);
148 148 }
149 149
150 150 void wait_dialog()
151 151 {
152 152 kill_error_report();
153 153 if(check_ntvdm_dialog()) {
154 154 fprintf(stderr,"Some dialog opens; please MANUALLY kill it.");
155 155 fflush(stderr);
156 156 do {
157 157 Sleep(1000);
158 158 } while(check_ntvdm_dialog());
159 159 fprintf(stderr,"... done\n");
160 160 }
161 161 }
162 162
163 163 void setstartupinfo(STARTUPINFO *si, char *inname, char *outname)
164 164 {
165 165 SECURITY_ATTRIBUTES sa;
166 166
167 167 ZeroMemory(&sa, sizeof(sa));
168 168 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
169 169 sa.lpSecurityDescriptor = NULL;
170 170 sa.bInheritHandle = TRUE;
171 171
172 172 si->dwFlags = STARTF_USESTDHANDLES;
173 173 if((inname!=0) && (strcmp(inname,"-")!=0)) {
174 174 si->hStdInput = CreateFile(inname,
175 175 FILE_READ_DATA,
176 176 FILE_SHARE_READ,
177 177 &sa,
178 178 OPEN_EXISTING,
179 179 FILE_ATTRIBUTE_NORMAL,
180 180 NULL);
181 181 } else
182 182 si->hStdInput = NULL;
183 183
184 184 if((outname!=0) && (strcmp(outname,"-")!=0)) {
185 185 si->hStdOutput = CreateFile(outname,
186 186 FILE_WRITE_DATA,
187 187 FILE_SHARE_READ,
188 188 &sa,
189 189 CREATE_ALWAYS,
190 190 FILE_ATTRIBUTE_NORMAL,
191 191 NULL);
192 192 } else
193 193 si->hStdOutput = NULL;
194 194
195 195 si->hStdError = NULL;
196 196 }
197 197
198 198 // taken from http://msdn.microsoft.com/en-us/library/ms682050(VS.85).aspx
199 199 void PrintMemoryInfo(DWORD processID)
200 200 {
201 201 HANDLE hProcess;
202 202 PROCESS_MEMORY_COUNTERS pmc;
203 203
204 204 // Print the process identifier.
205 205
206 206 printf("\nProcess ID: %u\n", processID);
207 207
208 208 // Print information about the memory usage of the process.
209 209
210 210 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
211 211 PROCESS_VM_READ,
212 212 FALSE,processID);
213 213 if(hProcess == NULL)
214 214 return;
215 215
216 216 if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
217 217 printf("\tPageFaultCount: %d\n",pmc.PageFaultCount);
218 218 printf("\tPeakWorkingSetSize: %d\n",
219 219 pmc.PeakWorkingSetSize);
220 220 printf("\tWorkingSetSize: %d\n",pmc.WorkingSetSize);
221 221 printf("\tQuotaPeakPagedPoolUsage: %d\n",
222 222 pmc.QuotaPeakPagedPoolUsage);
223 223 printf("\tQuotaPagedPoolUsage: %d\n",
224 224 pmc.QuotaPagedPoolUsage);
225 225 printf("\tQuotaPeakNonPagedPoolUsage: %d\n",
226 226 pmc.QuotaPeakNonPagedPoolUsage);
227 227 printf("\tQuotaNonPagedPoolUsage: %d\n",
228 228 pmc.QuotaNonPagedPoolUsage);
229 229 printf("\tPagefileUsage: %d\n",pmc.PagefileUsage);
230 230 printf("\tPeakPagefileUsage: %d\n",
231 231 pmc.PeakPagefileUsage);
232 232 }
233 233 CloseHandle( hProcess );
234 234 }
235 235
236 236 int check_memory_usage(DWORD pid, int max_mem, int *actual_usage) {
237 237 // modified from http://msdn.microsoft.com/en-us/library/ms682050(VS.85).aspx
238 238 //PrintMemoryInfo(pid);
239 239 HANDLE hProcess;
240 240 PROCESS_MEMORY_COUNTERS pmc;
241 241
242 242 if((max_mem==0) || (pid==0))
243 243 return 1;
244 244
245 245 if(pid == get_ntvdm_pid()) {
246 246 fprintf(stderr,"ntvdm: ignored\n");
247 247 return 1;
248 248 }
249 249
250 250 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
251 251 PROCESS_VM_READ,
252 252 FALSE, pid);
253 253 if(hProcess == NULL)
254 254 return 1;
255 255
256 256 int max_mem_usage = 0;
257 257 if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
258 258 max_mem_usage = pmc.PeakWorkingSetSize;
259 259 if(pmc.PeakPagefileUsage > max_mem_usage)
260 260 max_mem_usage = pmc.PeakPagefileUsage;
261 261 }
262 262 CloseHandle(hProcess);
263 263 if(actual_usage != NULL)
264 264 (*actual_usage) = max_mem_usage;
265 265 return (max_mem_usage <= max_mem);
266 266 }
267 267
268 268 void report_stat(double time_used, int memory_used)
269 269 {
270 270 fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
271 271 time_used,
272 272 time_used, (double)0,
273 273 memory_used);
274 274 }
275 275
276 276 double get_process_time_usage(HANDLE hProcess)
277 277 {
278 278 FILETIME creation_time;
279 279 FILETIME exit_time;
280 280 FILETIME kernel_time;
281 281 FILETIME user_time;
282 282 GetProcessTimes(hProcess,
283 283 &creation_time,
284 284 &exit_time,
285 285 &kernel_time,
286 286 &user_time);
287 287
288 288 SYSTEMTIME sys_kernel_time;
289 289 SYSTEMTIME sys_user_time;
290 290 FileTimeToSystemTime(&kernel_time, &sys_kernel_time);
291 291 FileTimeToSystemTime(&user_time, &sys_user_time);
292 292
293 293 double time_used =
294 294 ((sys_kernel_time.wSecond + sys_kernel_time.wMilliseconds/1000.0) +
295 295 (sys_user_time.wSecond + sys_user_time.wMilliseconds/1000.0));
296 296 return time_used;
297 297 }
298 298
299 299 int execute(char *exname, char *inname, char *outname, double t, int max_mem)
300 300 {
301 301 STARTUPINFO si;
302 302 PROCESS_INFORMATION pi;
303 303 int ifsuccess = EXE_RESULT_OK;
304 304
305 305 ZeroMemory(&si, sizeof(si));
306 306 si.cb = sizeof(si);
307 307 ZeroMemory(&pi, sizeof(pi));
308 308
309 309 setstartupinfo(&si, inname, outname);
310 310
311 311 if(!CreateProcess( NULL, // No module name (use command line).
312 312 TEXT(exname), // Command line.
313 313 NULL, // Process handle not inheritable.
314 314 NULL, // Thread handle not inheritable.
315 315 TRUE, // Set handle inheritance to FALSE.
316 316 0, // No creation flags.
317 317 NULL, // Use parent's environment block.
318 318 NULL, // Use parent's starting directory.
319 319 &si, // Pointer to STARTUPINFO structure.
320 320 &pi)) // Pointer to PROCESS_INFORMATION structure.
321 321 {
322 322 //printf( "CreateProcess failed (%d).\n", GetLastError() );
323 323 fprintf(stderr, "Process creation error.\n");
324 324 report_stat(0,0);
325 325 return EXE_RESULT_ERROR;
326 326 }
327 327 //fprintf(stderr,"Process ID: %ld\n",pi.dwProcessId);
328 328 //fprintf(stderr,"time limit = %d\n",t);
329 329
330 330 // checking memory usage
331 331 // wait 0.1 sec before checking mem usage
332 332
333 333 SetProcessWorkingSetSize(pi.hProcess,
334 334 1,
335 335 max_mem);
336 336 int actual_memory_usage = 0;
337 337
338 338 Sleep(INITIAL_WAIT_FOR_MEM_CHECK);
339 339 if(!check_memory_usage(pi.dwProcessId,max_mem,&actual_memory_usage)) {
340 340 // using too much memory
341 341 fprintf(stderr,"Memory limit exceeded.\n");
342 342 //PrintMemoryInfo(pi.dwProcessId);
343 343 ifsuccess = EXE_RESULT_MEMORY;
344 344 }
345 345
346 - if((ifsuccess == EXE_RESULT_MEMORY) ||
347 - (WaitForSingleObject(pi.hProcess,
348 - (int)(t*1000) + 1
349 - - INITIAL_WAIT_FOR_MEM_CHECK)==WAIT_TIMEOUT)) {
346 + //printf("PID: %d\n", pi.dwProcessId);
347 +
348 + if(ifsuccess != EXE_RESULT_MEMORY) {
349 + int based_time = (int)(t*1000) + 1 - INITIAL_WAIT_FOR_MEM_CHECK;
350 + bool major_timed_out = (WaitForSingleObject(pi.hProcess,
351 + based_time)==WAIT_TIMEOUT);
352 + if(major_timed_out) {
353 + // wait some more for user time.
354 + double time_used = get_process_time_usage(pi.hProcess);
355 + while(time_used <= t) {
356 + int iter_time = 100;
357 + if(t - time_used < 200)
358 + iter_time = 20;
359 + bool iter_timed_out = (WaitForSingleObject(pi.hProcess,
360 + iter_time)==WAIT_TIMEOUT);
361 + if(!iter_timed_out)
362 + break;
363 +
364 + time_used = get_process_time_usage(pi.hProcess);
365 + //printf("%lf\n",time_used);
366 + }
367 + ifsuccess = EXE_RESULT_TIMEOUT;
368 + }
369 + }
370 +
371 + if((ifsuccess == EXE_RESULT_MEMORY) || (ifsuccess == EXE_RESULT_TIMEOUT)) {
350 372 // Kill process, because (1) it used too much memory, or (2) time limit
351 373 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
352 374
353 375 if(ifsuccess != EXE_RESULT_MEMORY)
354 376 fprintf(stderr,"Time limit exceeded.\n");
355 377 if(hProcess != NULL) {
356 378 fprintf(stderr,"killing pid: %ld\n",pi.dwProcessId);
357 379 TerminateProcess(hProcess, 0);
358 380 wait_dialog();
359 381 } else {
360 382 DWORD dwNtvdmId = get_ntvdm_pid();
361 383 fprintf(stderr,"killing (ntvdm) pid: %ld\n",dwNtvdmId);
362 384 if(dwNtvdmId!=0) {
363 385 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwNtvdmId);
364 386 TerminateProcess(hProcess, 0);
365 387 } else {
366 388 fprintf(stderr,"killing process error\n");
367 389 }
368 390
369 391 if(get_ntvdm_pid()!=0) {
370 392 fprintf(stderr,"killing error, ntvdm.exe still remains;");
371 393 fprintf(stderr,"please MANUALLY kill it.");
372 394 fflush(stderr);
373 395 do {
374 396 Sleep(1000);
375 397 } while(get_ntvdm_pid()!=0);
376 398 fprintf(stderr,"... done\n");
377 399 wait_dialog();
378 400 }
379 401 }
380 402 if(ifsuccess != EXE_RESULT_MEMORY)
381 403 ifsuccess = EXE_RESULT_TIMEOUT;
382 404 }
383 405
384 406 // check memory after terminated
385 407 if((ifsuccess==EXE_RESULT_OK) &&
386 408 (!check_memory_usage(pi.dwProcessId,max_mem, &actual_memory_usage))) {
387 409 // using too much memory
388 410 ifsuccess = EXE_RESULT_MEMORY;
389 411 }
390 412
391 413 // check return code
392 414 if(ifsuccess==EXE_RESULT_OK) {
393 415 DWORD exitcode;
394 416 GetExitCodeProcess(pi.hProcess, &exitcode);
395 417 if(exitcode!=0) {
396 418 fprintf(stderr,"Exit status %d.\n", (int)exitcode);
397 419 ifsuccess = EXE_RESULT_ERROR;
398 420 }
399 421 }
400 422
401 423 wait_dialog();
402 424
403 425 if(si.hStdInput!=NULL)
404 426 CloseHandle(si.hStdInput);
405 427 if(si.hStdOutput!=NULL)
406 428 CloseHandle(si.hStdOutput);
407 429
408 430 if(ifsuccess==EXE_RESULT_OK)
409 431 fprintf(stderr,"OK\n");
410 432 else if(ifsuccess==EXE_RESULT_TIMEOUT)
411 433 fprintf(stderr,"Time limit exceeded.\n");
412 434 else if(ifsuccess==EXE_RESULT_MEMORY)
413 435 fprintf(stderr,"Memory limit exceeded.\n");
414 436
415 437 double actual_time_usage = get_process_time_usage(pi.hProcess);
416 438 /*
417 439 if(ifsuccess==EXE_RESULT_TIMEOUT)
418 440 actual_time_usage = t+1;
419 441 else
420 442 actual_time_usage = t;
421 443 */
422 444
423 445 report_stat(actual_time_usage,
424 446 (actual_memory_usage + 1023)/1024);
425 447
426 448 return ifsuccess;
427 449 }
428 450
You need to be logged in to leave comments. Login now