1/* 2 Title: Main program 3 4 Copyright (c) 2000 5 Cambridge University Technical Services Limited 6 7 Further development copyright David C.J. Matthews 2001-12, 2015, 2017-19 8 9 This library is free software; you can redistribute it and/or 10 modify it under the terms of the GNU Lesser General Public 11 License version 2.1 as published by the Free Software Foundation. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 22*/ 23 24#ifdef HAVE_CONFIG_H 25#include "config.h" 26#elif defined(_WIN32) 27#include "winconfig.h" 28#else 29#error "No configuration file" 30#endif 31 32#ifdef HAVE_STDIO_H 33#include <stdio.h> 34#endif 35 36#ifdef HAVE_STDLIB_H 37#include <stdlib.h> 38#endif 39 40#ifdef HAVE_STDARG_H 41#include <stdarg.h> 42#endif 43 44#ifdef HAVE_STRING_H 45#include <string.h> 46#endif 47 48#ifdef HAVE_ASSERT_H 49#include <assert.h> 50#define ASSERT(x) assert(x) 51#else 52#define ASSERT(x) 0 53#endif 54 55#if (defined(_WIN32)) 56#include <tchar.h> 57#else 58#define _T(x) x 59#define _tcslen strlen 60#define _tcstol strtol 61#define _tcsncmp strncmp 62#define _tcschr strchr 63#endif 64 65#include "globals.h" 66#include "sys.h" 67#include "gc.h" 68#include "heapsizing.h" 69#include "run_time.h" 70#include "machine_dep.h" 71#include "version.h" 72#include "diagnostics.h" 73#include "processes.h" 74#include "mpoly.h" 75#include "scanaddrs.h" 76#include "save_vec.h" 77#include "../polyexports.h" 78#include "memmgr.h" 79#include "pexport.h" 80#include "polystring.h" 81#include "statistics.h" 82#include "noreturn.h" 83#include "savestate.h" 84 85#if (defined(_WIN32)) 86#include "winstartup.h" 87#include "winguiconsole.h" 88 89static const TCHAR *lpszServiceName = 0; // DDE service name 90#endif 91 92FILE *polyStdout, *polyStderr; // Redirected in the Windows GUI 93 94NORETURNFN(static void Usage(const char *message, ...)); 95 96 97struct _userOptions userOptions; 98 99time_t exportTimeStamp; 100 101enum { 102 OPT_HEAPMIN, 103 OPT_HEAPMAX, 104 OPT_HEAPINIT, 105 OPT_GCPERCENT, 106 OPT_RESERVE, 107 OPT_GCTHREADS, 108 OPT_DEBUGOPTS, 109 OPT_DEBUGFILE, 110 OPT_DDESERVICE, 111 OPT_CODEPAGE, 112 OPT_REMOTESTATS 113}; 114 115static struct __argtab { 116 const TCHAR *argName; 117 const char *argHelp; 118 unsigned argKey; 119} argTable[] = 120{ 121 { _T("-H"), "Initial heap size (MB)", OPT_HEAPINIT }, 122 { _T("--minheap"), "Minimum heap size (MB)", OPT_HEAPMIN }, 123 { _T("--maxheap"), "Maximum heap size (MB)", OPT_HEAPMAX }, 124 { _T("--gcpercent"), "Target percentage time in GC (1-99)", OPT_GCPERCENT }, 125 { _T("--stackspace"), "Space to reserve for thread stacks and C++ heap(MB)", OPT_RESERVE }, 126 { _T("--gcthreads"), "Number of threads to use for garbage collection", OPT_GCTHREADS }, 127 { _T("--debug"), "Debug options: checkmem, gc, x", OPT_DEBUGOPTS }, 128 { _T("--logfile"), "Logging file (default is to log to stdout)", OPT_DEBUGFILE }, 129#if (defined(_WIN32)) 130#ifdef UNICODE 131 { _T("--codepage"), "Code-page to use for file-names etc in Windows", OPT_CODEPAGE }, 132#endif 133 { _T("-pServiceName"), "DDE service name for remote interrupt in Windows", OPT_DDESERVICE } 134#else 135 { _T("--exportstats"), "Enable another process to read the statistics", OPT_REMOTESTATS } 136#endif 137}; 138 139static struct __debugOpts { 140 const TCHAR *optName; 141 const char *optHelp; 142 unsigned optKey; 143} debugOptTable[] = 144{ 145 { _T("checkmem"), "Perform additional debugging checks on memory", DEBUG_CHECK_OBJECTS }, 146 { _T("gc"), "Log summary garbage-collector information", DEBUG_GC }, 147 { _T("gcenhanced"), "Log enhanced garbage-collector information", DEBUG_GC_ENHANCED }, 148 { _T("gcdetail"), "Log detailed garbage-collector information", DEBUG_GC_DETAIL }, 149 { _T("memmgr"), "Memory manager information", DEBUG_MEMMGR }, 150 { _T("threads"), "Thread related information", DEBUG_THREADS }, 151 { _T("gctasks"), "Log multi-thread GC information", DEBUG_GCTASKS }, 152 { _T("heapsize"), "Log heap resizing data", DEBUG_HEAPSIZE }, 153 { _T("x"), "Log X-windows information", DEBUG_X}, 154 { _T("sharing"), "Information from PolyML.shareCommonData", DEBUG_SHARING}, 155 { _T("locks"), "Information about contended locks", DEBUG_CONTENTION}, 156 { _T("rts"), "General run-time system calls", DEBUG_RTSCALLS}, 157 { _T("saving"), "Saving and loading state; exporting", DEBUG_SAVING } 158}; 159 160// Parse a parameter that is meant to be a size. Returns the value as a number 161// of kilobytes. 162POLYUNSIGNED parseSize(const TCHAR *p, const TCHAR *arg) 163{ 164 POLYUNSIGNED result = 0; 165 if (*p < '0' || *p > '9') 166 // There must be at least one digit 167 Usage("Incomplete %s option\n", arg); 168 while (true) 169 { 170 result = result*10 + *p++ - '0'; 171 if (*p == 0) 172 { 173 // The default is megabytes 174 result *= 1024; 175 break; 176 } 177 if (*p == 'G' || *p == 'g') 178 { 179 result *= 1024 * 1024; 180 p++; 181 break; 182 } 183 if (*p == 'M' || *p == 'm') 184 { 185 result *= 1024; 186 p++; 187 break; 188 } 189 if (*p == 'K' || *p == 'k') 190 { 191 p++; 192 break; 193 } 194 if (*p < '0' || *p > '9') 195 break; 196 } 197 if (*p != 0) 198 Usage("Malformed %s option\n", arg); 199 // The sizes must not exceed the possible heap size. 200#ifdef POLYML32IN64 201 if (result > 16 * 1024 * 1024) 202 Usage("Value of %s option must not exceeed 16Gbytes\n", arg); 203#elif (SIZEOF_VOIDP == 4) 204 if (result > 4 * 1024 * 1024) 205 Usage("Value of %s option must not exceeed 4Gbytes\n", arg); 206#else 207 // For completion only! 208 if (result > (POLYUNSIGNED)8 * 1024 * 1024 * 1024 * 1024 * 1024) 209 Usage("Value of %s option must not exceeed 8Ebytes\n", arg); 210#endif 211 return result; 212} 213 214/* In the Windows version this is called from WinMain in Console.c */ 215int polymain(int argc, TCHAR **argv, exportDescription *exports) 216{ 217 POLYUNSIGNED minsize=0, maxsize=0, initsize=0; 218 unsigned gcpercent=0; 219 /* Get arguments. */ 220 memset(&userOptions, 0, sizeof(userOptions)); /* Reset it */ 221 userOptions.gcthreads = 0; // Default multi-threaded 222 223 if (polyStdout == 0) polyStdout = stdout; 224 if (polyStderr == 0) polyStderr = stderr; 225 226 // Get the program name for CommandLine.name. This is allowed to be a full path or 227 // just the last component so we return whatever the system provides. 228 if (argc > 0) 229 userOptions.programName = argv[0]; 230 else 231 userOptions.programName = _T(""); // Set it to a valid empty string 232 233 TCHAR *importFileName = 0; 234 debugOptions = 0; 235 236 userOptions.user_arg_count = 0; 237 userOptions.user_arg_strings = (TCHAR**)malloc(argc * sizeof(TCHAR*)); // Enough room for all of them 238 239 // Process the argument list removing those recognised by the RTS and adding the 240 // remainder to the user argument list. 241 for (int i = 1; i < argc; i++) 242 { 243 if (argv[i][0] == '-') 244 { 245 bool argUsed = false; 246 for (unsigned j = 0; j < sizeof(argTable)/sizeof(argTable[0]); j++) 247 { 248 size_t argl = _tcslen(argTable[j].argName); 249 if (_tcsncmp(argv[i], argTable[j].argName, argl) == 0) 250 { 251 const TCHAR *p = 0; 252 TCHAR *endp = 0; 253 if (argTable[j].argKey != OPT_REMOTESTATS) 254 { 255 if (_tcslen(argv[i]) == argl) 256 { // If it has used all the argument pick the next 257 i++; 258 p = argv[i]; 259 } 260 else 261 { 262 p = argv[i]+argl; 263 if (*p == '=') p++; // Skip an equals sign 264 } 265 if (i >= argc) 266 Usage("Incomplete %s option\n", argTable[j].argName); 267 } 268 switch (argTable[j].argKey) 269 { 270 case OPT_HEAPMIN: 271 minsize = parseSize(p, argTable[j].argName); 272 break; 273 case OPT_HEAPMAX: 274 maxsize = parseSize(p, argTable[j].argName); 275 break; 276 case OPT_HEAPINIT: 277 initsize = parseSize(p, argTable[j].argName); 278 break; 279 case OPT_GCPERCENT: 280 gcpercent = _tcstol(p, &endp, 10); 281 if (*endp != '\0') 282 Usage("Malformed %s option\n", argTable[j].argName); 283 if (gcpercent < 1 || gcpercent > 99) 284 { 285 Usage("%s argument must be between 1 and 99\n", argTable[j].argName); 286 gcpercent = 0; 287 } 288 break; 289 case OPT_RESERVE: 290 { 291 POLYUNSIGNED reserve = parseSize(p, argTable[j].argName); 292 if (reserve != 0) 293 gHeapSizeParameters.SetReservation(reserve); 294 break; 295 } 296 case OPT_GCTHREADS: 297 userOptions.gcthreads = _tcstol(p, &endp, 10); 298 if (*endp != '\0') 299 Usage("Incomplete %s option\n", argTable[j].argName); 300 break; 301 case OPT_DEBUGOPTS: 302 while (*p != '\0') 303 { 304 // Debug options are separated by commas 305 bool optFound = false; 306 const TCHAR *q = _tcschr(p, ','); 307 if (q == NULL) q = p+_tcslen(p); 308 for (unsigned k = 0; k < sizeof(debugOptTable)/sizeof(debugOptTable[0]); k++) 309 { 310 if (_tcslen(debugOptTable[k].optName) == (size_t)(q-p) && 311 _tcsncmp(p, debugOptTable[k].optName, q-p) == 0) 312 { 313 debugOptions |= debugOptTable[k].optKey; 314 optFound = true; 315 } 316 } 317 if (! optFound) 318 Usage("Unknown argument to --debug\n"); 319 if (*q == ',') p = q+1; else p = q; 320 } 321 if (debugOptions & DEBUG_GC_DETAIL) debugOptions |= DEBUG_GC_ENHANCED; 322 if (debugOptions & DEBUG_GC_ENHANCED) debugOptions |= DEBUG_GC; 323 break; 324 case OPT_DEBUGFILE: 325 SetLogFile(p); 326 break; 327#if (defined(_WIN32)) 328 case OPT_DDESERVICE: 329 // Set the name for the DDE service. This allows the caller to specify the 330 // service name to be used to send Interrupt "signals". 331 lpszServiceName = p; 332 break; 333#if (defined(UNICODE)) 334 case OPT_CODEPAGE: 335 if (! setWindowsCodePage(p)) 336 Usage("Unknown argument to --codepage. Use code page number or CP_ACP, CP_UTF8.\n"); 337 break; 338#endif 339#endif 340 case OPT_REMOTESTATS: 341 // If set we export the statistics on Unix. 342 globalStats.exportStats = true; 343 break; 344 } 345 argUsed = true; 346 break; 347 } 348 } 349 if (! argUsed) // Add it to the user args. 350 userOptions.user_arg_strings[userOptions.user_arg_count++] = argv[i]; 351 } 352 else if (exports == 0 && importFileName == 0) 353 importFileName = argv[i]; 354 else 355 userOptions.user_arg_strings[userOptions.user_arg_count++] = argv[i]; 356 } 357 358 if (!gMem.Initialise()) 359 Usage("Unable to initialise memory allocator\n"); 360 361 if (exports == 0 && importFileName == 0) 362 Usage("Missing import file name\n"); 363 364 // If the maximum is provided it must be not less than the minimum. 365 if (maxsize != 0 && maxsize < minsize) 366 Usage("Minimum heap size must not be more than maximum size\n"); 367 // The initial size must be not more than the maximum 368 if (maxsize != 0 && maxsize < initsize) 369 Usage("Initial heap size must not be more than maximum size\n"); 370 // The initial size must be not less than the minimum 371 if (initsize != 0 && initsize < minsize) 372 Usage("Initial heap size must not be less than minimum size\n"); 373 374 if (userOptions.gcthreads == 0) 375 { 376 // If the gcthreads option is missing or zero the default is to try to 377 // use as many threads as there are physical processors. The result may 378 // be zero in which case we use the number of processors. Because memory 379 // bandwidth is a limiting factor we want to avoid muliple GC threads on 380 // hyperthreaded "processors". 381 userOptions.gcthreads = NumberOfPhysicalProcessors(); 382 if (userOptions.gcthreads == 0) 383 userOptions.gcthreads = NumberOfProcessors(); 384 } 385 386 // Set the heap size if it has been provided otherwise use the default. 387 gHeapSizeParameters.SetHeapParameters(minsize, maxsize, initsize, gcpercent); 388 389#if (defined(_WIN32)) 390 SetupDDEHandler(lpszServiceName); // Windows: Start the DDE handler now we processed any service name. 391#endif 392 393 // Initialise the run-time system before creating the heap. 394 InitModules(); 395 CreateHeap(); 396 397 PolyObject *rootFunction = 0; 398 399 if (exports != 0) 400 rootFunction = InitHeaderFromExport(exports); 401 else 402 { 403 if (importFileName != 0) 404 rootFunction = ImportPortable(importFileName); 405 if (rootFunction == 0) 406 exit(1); 407 } 408 409 StartModules(); 410 411 // Set up the initial process to run the root function. 412 processes->BeginRootThread(rootFunction); 413 414 finish(0); 415 416 /*NOTREACHED*/ 417 return 0; /* just to keep lint happy */ 418} 419 420void Uninitialise(void) 421// Close down everything and free all resources. Stop any threads or timers. 422{ 423 StopModules(); 424} 425 426void finish (int n) 427{ 428 // Make sure we don't get any interrupts once the destructors are 429 // applied to globals or statics. 430 Uninitialise(); 431#if (defined(_WIN32)) 432 ExitThread(n); 433#else 434 exit (n); 435#endif 436} 437 438// Print a message and exit if an argument is malformed. 439void Usage(const char *message, ...) 440{ 441 va_list vl; 442 fprintf(polyStdout, "\n"); 443 va_start(vl, message); 444 vfprintf(polyStdout, message, vl); 445 va_end(vl); 446 447 for (unsigned j = 0; j < sizeof(argTable)/sizeof(argTable[0]); j++) 448 { 449#if (defined(_WIN32) && defined(UNICODE)) 450 fprintf(polyStdout, "%S <%s>\n", argTable[j].argName, argTable[j].argHelp); 451#else 452 fprintf(polyStdout, "%s <%s>\n", argTable[j].argName, argTable[j].argHelp); 453#endif 454 } 455 fprintf(polyStdout, "Debug options:\n"); 456 for (unsigned k = 0; k < sizeof(debugOptTable)/sizeof(debugOptTable[0]); k++) 457 { 458#if (defined(_WIN32) && defined(UNICODE)) 459 fprintf(polyStdout, "%S <%s>\n", debugOptTable[k].optName, debugOptTable[k].optHelp); 460#else 461 fprintf(polyStdout, "%s <%s>\n", debugOptTable[k].optName, debugOptTable[k].optHelp); 462#endif 463 } 464 fflush(polyStdout); 465 466#if (defined(_WIN32)) 467 if (useConsole) 468 { 469 MessageBox(hMainWindow, _T("Poly/ML has exited"), _T("Poly/ML"), MB_OK); 470 } 471#endif 472 exit (1); 473} 474 475// Return a string containing the argument names. Can be printed out in response 476// to a --help argument. It is up to the ML application to do that since it may well 477// want to produce information about any arguments it chooses to process. 478char *RTSArgHelp(void) 479{ 480 static char buff[2000]; 481 char *p = buff; 482 for (unsigned j = 0; j < sizeof(argTable)/sizeof(argTable[0]); j++) 483 { 484#if (defined(_WIN32) && defined(UNICODE)) 485 int spaces = sprintf(p, "%S <%s>\n", argTable[j].argName, argTable[j].argHelp); 486#else 487 int spaces = sprintf(p, "%s <%s>\n", argTable[j].argName, argTable[j].argHelp); 488#endif 489 p += spaces; 490 } 491 { 492 int spaces = sprintf(p, "Debug options:\n"); 493 p += spaces; 494 } 495 for (unsigned k = 0; k < sizeof(debugOptTable)/sizeof(debugOptTable[0]); k++) 496 { 497#if (defined(_WIN32) && defined(UNICODE)) 498 int spaces = sprintf(p, "%S <%s>\n", debugOptTable[k].optName, debugOptTable[k].optHelp); 499#else 500 int spaces = sprintf(p, "%s <%s>\n", debugOptTable[k].optName, debugOptTable[k].optHelp); 501#endif 502 p += spaces; 503 } 504 ASSERT((unsigned)(p - buff) < (unsigned)sizeof(buff)); 505 return buff; 506} 507