diff options
author | Xavier Leroy <xavier.leroy@inria.fr> | 2008-07-29 08:31:41 +0000 |
---|---|---|
committer | Xavier Leroy <xavier.leroy@inria.fr> | 2008-07-29 08:31:41 +0000 |
commit | 776ae225a0cc5fa44b9279f81d45e9fd3dfa3cca (patch) | |
tree | f647c865d1681b0b8678ebb0c53c731055064c03 | |
parent | df023f535b9b4bb051cbce6dc39ea3b835bb80f1 (diff) |
ocamldebug under Win32 (S. Le Gall, Lexifi)
git-svn-id: http://caml.inria.fr/svn/ocaml/trunk@8955 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02
-rw-r--r-- | Makefile.nt | 13 | ||||
-rw-r--r-- | README.win32 | 9 | ||||
-rw-r--r-- | byterun/Makefile.nt | 4 | ||||
-rw-r--r-- | byterun/debugger.c | 77 | ||||
-rw-r--r-- | config/Makefile.mingw | 10 | ||||
-rw-r--r-- | config/Makefile.msvc | 12 | ||||
-rw-r--r-- | config/Makefile.msvc64 | 6 | ||||
-rw-r--r-- | debugger/Makefile | 104 | ||||
-rw-r--r-- | debugger/Makefile.nt | 17 | ||||
-rw-r--r-- | debugger/Makefile.shared | 116 | ||||
-rw-r--r-- | debugger/command_line.ml | 14 | ||||
-rw-r--r-- | debugger/debugcom.ml | 11 | ||||
-rw-r--r-- | debugger/debugger_config.ml | 11 | ||||
-rw-r--r-- | debugger/exec.ml | 7 | ||||
-rw-r--r-- | debugger/main.ml | 11 | ||||
-rw-r--r-- | debugger/program_loading.ml | 39 | ||||
-rw-r--r-- | debugger/program_management.ml | 1 | ||||
-rw-r--r-- | debugger/unix_tools.ml | 4 | ||||
-rw-r--r-- | myocamlbuild.ml | 4 | ||||
-rw-r--r-- | otherlibs/win32unix/Makefile.nt | 5 | ||||
-rw-r--r-- | otherlibs/win32unix/dllunix.dlib | 1 | ||||
-rw-r--r-- | otherlibs/win32unix/libunix.clib | 1 | ||||
-rw-r--r-- | otherlibs/win32unix/select.c | 1076 | ||||
-rw-r--r-- | otherlibs/win32unix/startup.c | 11 |
24 files changed, 1356 insertions, 208 deletions
diff --git a/Makefile.nt b/Makefile.nt index 99d4b6cdc..855274bed 100644 --- a/Makefile.nt +++ b/Makefile.nt @@ -120,7 +120,7 @@ defaultentry: @echo "Please refer to the installation instructions in file README.win32." # Recompile the system using the bootstrap compiler -all: runtime ocamlc ocamllex ocamlyacc ocamltools library ocaml otherlibraries ocamldoc.byte ocamlbuild.byte camlp4out win32gui +all: runtime ocamlc ocamllex ocamlyacc ocamltools library ocaml otherlibraries ocamldoc.byte ocamlbuild.byte camlp4out $(DEBUGGER) win32gui # The compilation of ocaml will fail if the runtime has changed. # Never mind, just do make bootstrap to reach fixpoint again. @@ -232,6 +232,8 @@ installbyt: cd ocamldoc ; $(MAKEREC) install mkdir -p $(STUBLIBDIR) for i in $(OTHERLIBRARIES); do $(MAKEREC) -C otherlibs/$$i install; done + if test -f debugger/ocamldebug.exe; then (cd debugger; $(MAKEREC) install); \ + else :; fi cd win32caml ; $(MAKE) install ./build/partial-install.sh cp config/Makefile $(LIBDIR)/Makefile.config @@ -581,6 +583,15 @@ clean:: alldepend:: for i in $(OTHERLIBRARIES); do $(MAKEREC) -C otherlibs/$$i depend; done +# The replay debugger + +ocamldebugger: ocamlc ocamlyacc ocamllex + cd debugger; $(MAKEREC) all +partialclean:: + cd debugger; $(MAKEREC) clean +alldepend:: + cd debugger; $(MAKEREC) depend + # Camlp4 camlp4out: ocamlc otherlibraries ocamlbuild-partial-boot ocamlbuild.byte diff --git a/README.win32 b/README.win32 index 782aa120a..26a3e6703 100644 --- a/README.win32 +++ b/README.win32 @@ -22,7 +22,7 @@ Third-party software required Speed of bytecode interpreter 70% 100% 100% -Replay debugger no no yes +Replay debugger yes (**) yes (**) yes The Unix library partial partial full @@ -37,6 +37,9 @@ the GPL. Thus, these .exe files can only be distributed under a license that is compatible with the GPL. Executables generated by MSVC or by MinGW have no such restrictions. +(**) The debugger is supported but the "replay" function of it are not enabled. +Other functions are available (step, goto, run...). + The remainder of this document gives more information on each port. ------------------------------------------------------------------------------ @@ -150,7 +153,7 @@ Unix/GCC or Cygwin or Mingw on similar hardware. * Libraries available in this port: "num", "str", "threads", "graphics", "labltk", and large parts of "unix". -* The replay debugger is not supported. +* The replay debugger is partially supported. CREDITS: @@ -247,7 +250,7 @@ NOTES: * Libraries available in this port: "num", "str", "threads", "graphics", "labltk", and large parts of "unix". -* The replay debugger is not supported. +* The replay debugger is partially supported. ------------------------------------------------------------------------------ diff --git a/byterun/Makefile.nt b/byterun/Makefile.nt index 1dbda5852..455de6fa6 100644 --- a/byterun/Makefile.nt +++ b/byterun/Makefile.nt @@ -22,10 +22,10 @@ OBJS=$(COMMONOBJS:.o=.$(O)) win32.$(O) main.$(O) DOBJS=$(OBJS:.$(O)=.$(DBGO)) instrtrace.$(DBGO) ocamlrun$(EXE): libcamlrun.$(A) prims.$(O) - $(MKEXE) -o ocamlrun$(EXE) prims.$(O) libcamlrun.$(A) + $(MKEXE) -o ocamlrun$(EXE) prims.$(O) $(call SYSLIB,ws2_32) libcamlrun.$(A) ocamlrund$(EXE): libcamlrund.$(A) prims.$(O) main.$(O) - $(MKEXE) -o ocamlrun$(EXE) $(BYTECCDBGCOMPOPTS) prims.$(O) libcamlrund.$(A) + $(MKEXE) -o ocamlrun$(EXE) $(BYTECCDBGCOMPOPTS) prims.$(O) $(call SYSLIB,ws2_32) libcamlrund.$(A) libcamlrun.$(A): $(OBJS) $(call MKLIB,libcamlrun.$(A),$(OBJS)) diff --git a/byterun/debugger.c b/byterun/debugger.c index d6d3a2b5e..38b1923e6 100644 --- a/byterun/debugger.c +++ b/byterun/debugger.c @@ -15,6 +15,10 @@ /* Interface with the debugger */ +#ifdef _WIN32 +#include <io.h> +#endif /* _WIN32 */ + #include <string.h> #include "config.h" @@ -32,7 +36,7 @@ int caml_debugger_in_use = 0; uintnat caml_event_count; -#if !defined(HAS_SOCKETS) || defined(_WIN32) +#if !defined(HAS_SOCKETS) void caml_debugger_init(void) { @@ -49,17 +53,26 @@ void caml_debugger(enum event_kind event) #endif #include <errno.h> #include <sys/types.h> +#ifndef _WIN32 #include <sys/wait.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> +#else +#define ATOM ATOM_WS +#include <winsock.h> +#undef ATOM +#include <process.h> +#endif static int sock_domain; /* Socket domain for the debugger */ static union { /* Socket address for the debugger */ struct sockaddr s_gen; +#ifndef _WIN32 struct sockaddr_un s_unix; +#endif struct sockaddr_in s_inet; } sock_addr; static int sock_addr_len; /* Length of sock_addr */ @@ -72,16 +85,46 @@ static char *dbg_addr = "(none)"; static void open_connection(void) { +#ifdef _WIN32 + /* Set socket to synchronous mode so that file descriptor-oriented + functions (read()/write() etc.) can be used */ + + int oldvalue, oldvaluelen, newvalue, retcode; + oldvaluelen = sizeof(oldvalue); + retcode = getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, + (char *) &oldvalue, &oldvaluelen); + if (retcode == 0) { + newvalue = SO_SYNCHRONOUS_NONALERT; + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, + (char *) &newvalue, sizeof(newvalue)); + } +#endif dbg_socket = socket(sock_domain, SOCK_STREAM, 0); +#ifdef _WIN32 + if (retcode == 0) { + /* Restore initial mode */ + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, + (char *) &oldvalue, oldvaluelen); + } +#endif if (dbg_socket == -1 || connect(dbg_socket, &sock_addr.s_gen, sock_addr_len) == -1){ caml_fatal_error_arg2 ("cannot connect to debugger at %s", dbg_addr, "error: %s\n", strerror (errno)); } +#ifdef _WIN32 + dbg_socket = _open_osfhandle(dbg_socket, 0); + if (dbg_socket == -1) + caml_fatal_error("_open_osfhandle failed"); +#endif dbg_in = caml_open_descriptor_in(dbg_socket); dbg_out = caml_open_descriptor_out(dbg_socket); if (!caml_debugger_in_use) caml_putword(dbg_out, -1); /* first connection */ +#ifdef _WIN32 + caml_putword(dbg_out, _getpid()); +#else caml_putword(dbg_out, getpid()); +#endif caml_flush(dbg_out); } @@ -92,6 +135,20 @@ static void close_connection(void) dbg_socket = -1; /* was closed by caml_close_channel */ } +#ifdef _WIN32 +static void winsock_startup(void) +{ + WSADATA wsaData; + int err = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (err) caml_fatal_error("WSAStartup failed"); +} + +static void winsock_cleanup(void) +{ + WSACleanup(); +} +#endif + void caml_debugger_init(void) { char * address; @@ -103,12 +160,17 @@ void caml_debugger_init(void) if (address == NULL) return; dbg_addr = address; +#ifdef _WIN32 + winsock_startup(); + (void)atexit(winsock_cleanup); +#endif /* Parse the address */ port = NULL; for (p = address; *p != 0; p++) { if (*p == ':') { *p = 0; port = p+1; break; } } if (port == NULL) { +#ifndef _WIN32 /* Unix domain */ sock_domain = PF_UNIX; sock_addr.s_unix.sun_family = AF_UNIX; @@ -117,6 +179,9 @@ void caml_debugger_init(void) sock_addr_len = ((char *)&(sock_addr.s_unix.sun_path) - (char *)&(sock_addr.s_unix)) + strlen(address); +#else + caml_fatal_error("Unix sockets not supported"); +#endif } else { /* Internet domain */ sock_domain = PF_INET; @@ -241,6 +306,7 @@ void caml_debugger(enum event_kind event) caml_set_instruction(caml_start_code + pos, caml_saved_code[pos]); break; case REQ_CHECKPOINT: +#ifndef _WIN32 i = fork(); if (i == 0) { close_connection(); /* Close parent connection. */ @@ -249,6 +315,10 @@ void caml_debugger(enum event_kind event) caml_putword(dbg_out, i); caml_flush(dbg_out); } +#else + caml_fatal_error("error: REQ_CHECKPOINT command"); + exit(-1); +#endif break; case REQ_GO: caml_event_count = caml_getword(dbg_in); @@ -257,7 +327,12 @@ void caml_debugger(enum event_kind event) exit(0); break; case REQ_WAIT: +#ifndef _WIN32 wait(NULL); +#else + caml_fatal_error("Fatal error: REQ_WAIT command"); + exit(-1); +#endif break; case REQ_INITIAL_FRAME: frame = caml_extern_sp + 1; diff --git a/config/Makefile.mingw b/config/Makefile.mingw index 1583ccebd..0a2bb0a03 100644 --- a/config/Makefile.mingw +++ b/config/Makefile.mingw @@ -66,7 +66,7 @@ ASPP=gcc ASPPPROFFLAGS= PROFILING=noprof DYNLINKOPTS= -DEBUGGER= +DEBUGGER=ocamldebugger CC_PROFILE= SYSTHREAD_SUPPORT=true EXTRALIBS= @@ -87,8 +87,8 @@ BYTECCLINKOPTS= DLLCCCOMPOPTS=-O -mms-bitfields -Wall -Wno-unused -DCAML_DLL ### Libraries needed -BYTECCLIBS= -NATIVECCLIBS= +BYTECCLIBS=-lws2_32 +NATIVECCLIBS=-lws2_32 ### How to invoke the C preprocessor CPP=$(BYTECC) -E @@ -149,8 +149,8 @@ BNG_ASM_LEVEL=1 # There must be no spaces or special characters in $(TK_ROOT) TK_ROOT=c:/tcl TK_DEFS=-I$(TK_ROOT)/include -TK_LINK=$(TK_ROOT)/bin/tk83.dll $(TK_ROOT)/bin/tcl83.dll -lwsock32 -#TK_LINK=$(TK_ROOT)/lib/tk84.lib $(TK_ROOT)/lib/tcl84.lib -lwsock32 +TK_LINK=$(TK_ROOT)/bin/tk83.dll $(TK_ROOT)/bin/tcl83.dll -lws2_32 +#TK_LINK=$(TK_ROOT)/lib/tk84.lib $(TK_ROOT)/lib/tcl84.lib -lws2_32 ############# Aliases for common commands diff --git a/config/Makefile.msvc b/config/Makefile.msvc index aafcf0a60..a16b86c4d 100644 --- a/config/Makefile.msvc +++ b/config/Makefile.msvc @@ -65,7 +65,7 @@ ASPP= ASPPPROFFLAGS= PROFILING=noprof DYNLINKOPTS= -DEBUGGER= +DEBUGGER=ocamldebugger CC_PROFILE= SYSTHREAD_SUPPORT=true EXTRALIBS= @@ -86,8 +86,8 @@ BYTECCLINKOPTS=/MD /F16777216 DLLCCCOMPOPTS=/Ox /MD ### Libraries needed -BYTECCLIBS=advapi32.lib -NATIVECCLIBS=advapi32.lib +BYTECCLIBS=advapi32.lib ws2_32.lib +NATIVECCLIBS=advapi32.lib ws2_32.lib ### How to invoke the C preprocessor CPP=cl /nologo /EP @@ -152,11 +152,11 @@ TK_DEFS=-I$(TK_ROOT)/include # produced by OCaml, and is therefore required for binary distribution # of these libraries. However, $(TK_ROOT) must be added to the LIB # environment variable, as described in README.win32. -#TK_LINK=tk84.lib tcl84.lib wsock32.lib -TK_LINK=tk83.lib tcl83.lib wsock32.lib +#TK_LINK=tk84.lib tcl84.lib ws2_32.lib +TK_LINK=tk83.lib tcl83.lib ws2_32.lib # An alternative definition that avoids mucking with the LIB variable, # but hard-wires the Tcl/Tk location in the binaries -# TK_LINK=$(TK_ROOT)/tk83.lib $(TK_ROOT)/tcl83.lib wsock32.lib +# TK_LINK=$(TK_ROOT)/tk83.lib $(TK_ROOT)/tcl83.lib ws2_32.lib ############# Aliases for common commands diff --git a/config/Makefile.msvc64 b/config/Makefile.msvc64 index cef571117..a067fd065 100644 --- a/config/Makefile.msvc64 +++ b/config/Makefile.msvc64 @@ -66,7 +66,7 @@ ASPP= ASPPPROFFLAGS= PROFILING=noprof DYNLINKOPTS= -DEBUGGER= +DEBUGGER=ocamldebugger CC_PROFILE= SYSTHREAD_SUPPORT=true CMXS=cmxs @@ -90,8 +90,8 @@ DLLCCCOMPOPTS=/Ox /MD ### Libraries needed EXTRALIBS=bufferoverflowu.lib -BYTECCLIBS=advapi32.lib $(EXTRALIBS) -NATIVECCLIBS=advapi32.lib $(EXTRALIBS) +BYTECCLIBS=advapi32.lib ws2_32.lib $(EXTRALIBS) +NATIVECCLIBS=advapi32.lib ws2_32.lib $(EXTRALIBS) ### How to invoke the C preprocessor CPP=cl /nologo /EP diff --git a/debugger/Makefile b/debugger/Makefile index a1faabd1f..3ff1b54aa 100644 --- a/debugger/Makefile +++ b/debugger/Makefile @@ -12,105 +12,5 @@ # $Id$ -include ../config/Makefile - -CAMLC=../ocamlcomp.sh -COMPFLAGS=-warn-error A $(INCLUDES) -LINKFLAGS=-linkall -I ../otherlibs/unix -CAMLYACC=../boot/ocamlyacc -YACCFLAGS= -CAMLLEX=../boot/ocamlrun ../boot/ocamllex -CAMLDEP=../boot/ocamlrun ../tools/ocamldep -DEPFLAGS=$(INCLUDES) - -INCLUDES=\ - -I ../utils -I ../parsing -I ../typing -I ../bytecomp -I ../toplevel \ - -I ../otherlibs/unix - -OTHEROBJS=\ - ../otherlibs/unix/unix.cma \ - ../utils/misc.cmo ../utils/config.cmo \ - ../utils/tbl.cmo ../utils/clflags.cmo ../utils/consistbl.cmo \ - ../parsing/longident.cmo \ - ../typing/ident.cmo ../typing/path.cmo ../typing/types.cmo \ - ../typing/btype.cmo ../typing/primitive.cmo ../typing/typedtree.cmo \ - ../typing/subst.cmo ../typing/predef.cmo \ - ../typing/datarepr.cmo ../typing/env.cmo ../typing/oprint.cmo \ - ../typing/ctype.cmo ../typing/printtyp.cmo ../typing/mtype.cmo \ - ../bytecomp/runtimedef.cmo ../bytecomp/bytesections.cmo \ - ../bytecomp/dll.cmo ../bytecomp/meta.cmo ../bytecomp/symtable.cmo \ - ../bytecomp/opcodes.cmo \ - ../toplevel/genprintval.cmo - - -OBJS=\ - dynlink.cmo \ - int64ops.cmo \ - primitives.cmo \ - unix_tools.cmo \ - debugger_config.cmo \ - envaux.cmo \ - parameters.cmo \ - lexer.cmo \ - input_handling.cmo \ - question.cmo \ - debugcom.cmo \ - exec.cmo \ - source.cmo \ - pos.cmo \ - checkpoints.cmo \ - events.cmo \ - symbols.cmo \ - breakpoints.cmo \ - trap_barrier.cmo \ - history.cmo \ - program_loading.cmo \ - printval.cmo \ - show_source.cmo \ - time_travel.cmo \ - program_management.cmo \ - frames.cmo \ - eval.cmo \ - show_information.cmo \ - loadprinter.cmo \ - parser.cmo \ - command_line.cmo \ - main.cmo - -all: ocamldebug$(EXE) - -ocamldebug$(EXE): $(OBJS) $(OTHEROBJS) - $(CAMLC) $(LINKFLAGS) -o ocamldebug$(EXE) -linkall $(OTHEROBJS) $(OBJS) - -install: - cp ocamldebug$(EXE) $(BINDIR)/ocamldebug$(EXE) - -clean:: - rm -f ocamldebug$(EXE) - rm -f *.cmo *.cmi - -.SUFFIXES: -.SUFFIXES: .ml .cmo .mli .cmi - -.ml.cmo: - $(CAMLC) -c $(COMPFLAGS) $< - -.mli.cmi: - $(CAMLC) -c $(COMPFLAGS) $< - -depend: beforedepend - $(CAMLDEP) $(DEPFLAGS) *.mli *.ml > .depend - -lexer.ml: lexer.mll - $(CAMLLEX) lexer.mll -clean:: - rm -f lexer.ml -beforedepend:: lexer.ml - -parser.ml parser.mli: parser.mly - $(CAMLYACC) parser.mly -clean:: - rm -f parser.ml parser.mli -beforedepend:: parser.ml parser.mli - -include .depend +UNIXDIR=../otherlibs/unix +include Makefile.shared diff --git a/debugger/Makefile.nt b/debugger/Makefile.nt new file mode 100644 index 000000000..3630d32e0 --- /dev/null +++ b/debugger/Makefile.nt @@ -0,0 +1,17 @@ +######################################################################### +# # +# Objective Caml # +# # +# Xavier Leroy, projet Cristal, INRIA Rocquencourt # +# # +# Copyright 1999 Institut National de Recherche en Informatique et # +# en Automatique. All rights reserved. This file is distributed # +# under the terms of the Q Public License version 1.0. # +# # +######################################################################### + +# $Id$ + +UNIXDIR=../otherlibs/win32unix +include Makefile.shared + diff --git a/debugger/Makefile.shared b/debugger/Makefile.shared new file mode 100644 index 000000000..4ed986a54 --- /dev/null +++ b/debugger/Makefile.shared @@ -0,0 +1,116 @@ +######################################################################### +# # +# Objective Caml # +# # +# Xavier Leroy, projet Cristal, INRIA Rocquencourt # +# # +# Copyright 1999 Institut National de Recherche en Informatique et # +# en Automatique. All rights reserved. This file is distributed # +# under the terms of the Q Public License version 1.0. # +# # +######################################################################### + +# $Id$ + +include ../config/Makefile + +CAMLC=../ocamlcomp.sh +COMPFLAGS=-warn-error A $(INCLUDES) +LINKFLAGS=-linkall -I $(UNIXDIR) +CAMLYACC=../boot/ocamlyacc +YACCFLAGS= +CAMLLEX=../boot/ocamlrun ../boot/ocamllex +CAMLDEP=../boot/ocamlrun ../tools/ocamldep +DEPFLAGS=$(INCLUDES) + +INCLUDES=\ + -I ../utils -I ../parsing -I ../typing -I ../bytecomp -I ../toplevel \ + -I $(UNIXDIR) + +OTHEROBJS=\ + $(UNIXDIR)/unix.cma \ + ../utils/misc.cmo ../utils/config.cmo \ + ../utils/tbl.cmo ../utils/clflags.cmo ../utils/consistbl.cmo \ + ../parsing/longident.cmo \ + ../typing/ident.cmo ../typing/path.cmo ../typing/types.cmo \ + ../typing/btype.cmo ../typing/primitive.cmo ../typing/typedtree.cmo \ + ../typing/subst.cmo ../typing/predef.cmo \ + ../typing/datarepr.cmo ../typing/env.cmo ../typing/oprint.cmo \ + ../typing/ctype.cmo ../typing/printtyp.cmo ../typing/mtype.cmo \ + ../bytecomp/runtimedef.cmo ../bytecomp/bytesections.cmo \ + ../bytecomp/dll.cmo ../bytecomp/meta.cmo ../bytecomp/symtable.cmo \ + ../bytecomp/opcodes.cmo \ + ../toplevel/genprintval.cmo + + +OBJS=\ + dynlink.cmo \ + int64ops.cmo \ + primitives.cmo \ + unix_tools.cmo \ + debugger_config.cmo \ + envaux.cmo \ + parameters.cmo \ + lexer.cmo \ + input_handling.cmo \ + question.cmo \ + debugcom.cmo \ + exec.cmo \ + source.cmo \ + pos.cmo \ + checkpoints.cmo \ + events.cmo \ + symbols.cmo \ + breakpoints.cmo \ + trap_barrier.cmo \ + history.cmo \ + program_loading.cmo \ + printval.cmo \ + show_source.cmo \ + time_travel.cmo \ + program_management.cmo \ + frames.cmo \ + eval.cmo \ + show_information.cmo \ + loadprinter.cmo \ + parser.cmo \ + command_line.cmo \ + main.cmo + +all: ocamldebug$(EXE) + +ocamldebug$(EXE): $(OBJS) $(OTHEROBJS) + $(CAMLC) $(LINKFLAGS) -o ocamldebug$(EXE) -linkall $(OTHEROBJS) $(OBJS) + +install: + cp ocamldebug$(EXE) $(BINDIR)/ocamldebug$(EXE) + +clean:: + rm -f ocamldebug$(EXE) + rm -f *.cmo *.cmi + +.SUFFIXES: +.SUFFIXES: .ml .cmo .mli .cmi + +.ml.cmo: + $(CAMLC) -c $(COMPFLAGS) $< + +.mli.cmi: + $(CAMLC) -c $(COMPFLAGS) $< + +depend: beforedepend + $(CAMLDEP) $(DEPFLAGS) *.mli *.ml > .depend + +lexer.ml: lexer.mll + $(CAMLLEX) lexer.mll +clean:: + rm -f lexer.ml +beforedepend:: lexer.ml + +parser.ml parser.mli: parser.mly + $(CAMLYACC) parser.mly +clean:: + rm -f parser.ml parser.mli +beforedepend:: parser.ml parser.mli + +include .depend diff --git a/debugger/command_line.ml b/debugger/command_line.ml index cfbdee303..f37d529b3 100644 --- a/debugger/command_line.ml +++ b/debugger/command_line.ml @@ -76,6 +76,13 @@ let error text = eprintf "%s@." text; raise Toplevel +let check_not_windows feature = + match Sys.os_type with + | "Win32" -> + error ("'"^feature^"' feature not supported on Windows") + | _ -> + () + let eol = end_of_line Lexer.lexeme @@ -220,7 +227,7 @@ let instr_shell ppf lexbuf = let instr_pwd ppf lexbuf = eol lexbuf; - ignore(system "/bin/pwd") + fprintf ppf "%s@." (Sys.getcwd ()) let instr_dir ppf lexbuf = let new_directory = argument_list_eol argument lexbuf in @@ -254,6 +261,7 @@ let instr_run ppf lexbuf = let instr_reverse ppf lexbuf = eol lexbuf; + check_not_windows "reverse"; ensure_loaded (); reset_named_values(); back_run (); @@ -276,6 +284,7 @@ let instr_back ppf lexbuf = | None -> _1 | Some x -> x in + check_not_windows "backstep"; ensure_loaded (); reset_named_values(); step (_0 -- step_count); @@ -301,6 +310,7 @@ let instr_next ppf lexbuf = let instr_start ppf lexbuf = eol lexbuf; + check_not_windows "start"; ensure_loaded (); reset_named_values(); start (); @@ -312,6 +322,7 @@ let instr_previous ppf lexbuf = | None -> 1 | Some x -> x in + check_not_windows "previous"; ensure_loaded (); reset_named_values(); previous step_count; @@ -672,6 +683,7 @@ let instr_last ppf lexbuf = | None -> _1 | Some x -> x in + check_not_windows "last"; reset_named_values(); go_to (History.previous_time count); show_current_event ppf diff --git a/debugger/debugcom.ml b/debugger/debugcom.ml index edec45472..307f4258d 100644 --- a/debugger/debugcom.ml +++ b/debugger/debugcom.ml @@ -99,10 +99,13 @@ let rec do_go n = (* Perform a checkpoint *) let do_checkpoint () = - output_char !conn.io_out 'c'; - flush !conn.io_out; - let pid = input_binary_int !conn.io_in in - if pid = -1 then Checkpoint_failed else Checkpoint_done pid + match Sys.os_type with + "Win32" -> failwith "do_checkpoint" + | _ -> + output_char !conn.io_out 'c'; + flush !conn.io_out; + let pid = input_binary_int !conn.io_in in + if pid = -1 then Checkpoint_failed else Checkpoint_done pid (* Kill the given process. *) let stop chan = diff --git a/debugger/debugger_config.ml b/debugger/debugger_config.ml index fa6fd7018..13e3f086c 100644 --- a/debugger/debugger_config.ml +++ b/debugger/debugger_config.ml @@ -51,7 +51,10 @@ let event_mark_before = "<|b|>" let event_mark_after = "<|a|>" (* Name of shell used to launch the debuggee *) -let shell = "/bin/sh" +let shell = + match Sys.os_type with + "Win32" -> "cmd" + | _ -> "/bin/sh" (* Name of the Objective Caml runtime. *) let runtime_program = "ocamlrun" @@ -71,5 +74,7 @@ let checkpoint_small_step = ref (~~ "1000") let checkpoint_max_count = ref 15 (* Whether to keep checkpoints or not. *) -let make_checkpoints = ref true - +let make_checkpoints = ref + (match Sys.os_type with + "Win32" -> false + | _ -> true) diff --git a/debugger/exec.ml b/debugger/exec.ml index d97a8c4e7..1ea165978 100644 --- a/debugger/exec.ml +++ b/debugger/exec.ml @@ -25,8 +25,11 @@ let break signum = else raise Sys.Break let _ = - Sys.set_signal Sys.sigint (Sys.Signal_handle break); - Sys.set_signal Sys.sigpipe (Sys.Signal_handle (fun _ -> raise End_of_file)) + match Sys.os_type with + "Win32" -> () + | _ -> + Sys.set_signal Sys.sigint (Sys.Signal_handle break); + Sys.set_signal Sys.sigpipe (Sys.Signal_handle (fun _ -> raise End_of_file)) let protect f = if !is_protected then diff --git a/debugger/main.ml b/debugger/main.ml index 4920d0d79..fda242bc5 100644 --- a/debugger/main.ml +++ b/debugger/main.ml @@ -148,8 +148,15 @@ let speclist = [ let main () = try - socket_name := Filename.concat Filename.temp_dir_name - ("camldebug" ^ (string_of_int (Unix.getpid ()))); + socket_name := + (match Sys.os_type with + "Win32" -> + (Unix.string_of_inet_addr Unix.inet_addr_loopback)^ + ":"^ + (string_of_int (10000 + ((Unix.getpid ()) mod 10000))) + | _ -> Filename.concat Filename.temp_dir_name + ("camldebug" ^ (string_of_int (Unix.getpid ()))) + ); begin try Arg.parse speclist anonymous ""; Arg.usage speclist diff --git a/debugger/program_loading.ml b/debugger/program_loading.ml index e1507e5d9..1a750a2bb 100644 --- a/debugger/program_loading.ml +++ b/debugger/program_loading.ml @@ -37,7 +37,7 @@ let load_program () = (*** Launching functions. ***) (* A generic function for launching the program *) -let generic_exec cmdline = function () -> +let generic_exec_unix cmdline = function () -> if !debug_loading then prerr_endline "Launching program..."; let child = @@ -64,11 +64,36 @@ let generic_exec cmdline = function () -> (_, WEXITED 0) -> () | _ -> raise Toplevel +let generic_exec_win cmdline = function () -> + if !debug_loading then + prerr_endline "Launching program..."; + try ignore(create_process "cmd.exe" [| "/C"; cmdline() |] stdin stdout stderr) + with x -> + Unix_tools.report_error x; + raise Toplevel + +let generic_exec = + match Sys.os_type with + "Win32" -> generic_exec_win + | _ -> generic_exec_unix + (* Execute the program by calling the runtime explicitely *) let exec_with_runtime = generic_exec (function () -> - Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s %s" + match Sys.os_type with + "Win32" -> + (* This fould fail on a file name with spaces + but quoting is even worse because Unix.create_process + thinks each command line parameter is a file. + So no good solution so far *) + Printf.sprintf "set CAML_DEBUG_SOCKET=%s && %s %s %s" + !socket_name + runtime_program + !program_name + !arguments + | _ -> + Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s %s" !socket_name (Filename.quote runtime_program) (Filename.quote !program_name) @@ -78,7 +103,15 @@ let exec_with_runtime = let exec_direct = generic_exec (function () -> - Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s" + match Sys.os_type with + "Win32" -> + (* See the comment above *) + Printf.sprintf "set CAML_DEBUG_SOCKET=%s && %s %s" + !socket_name + !program_name + !arguments + | _ -> + Printf.sprintf "CAML_DEBUG_SOCKET=%s %s %s" !socket_name (Filename.quote !program_name) !arguments) diff --git a/debugger/program_management.ml b/debugger/program_management.ml index d38adc713..35f74d654 100644 --- a/debugger/program_management.ml +++ b/debugger/program_management.ml @@ -74,6 +74,7 @@ let open_connection address continue = let sock = socket sock_domain SOCK_STREAM 0 in (try bind sock sock_address; + setsockopt sock SO_REUSEADDR true; listen sock 3; connection := io_channel_of_descr sock; Input_handling.add_file !connection (accept_connection continue); diff --git a/debugger/unix_tools.ml b/debugger/unix_tools.ml index 5061bb1dd..5328a2aad 100644 --- a/debugger/unix_tools.ml +++ b/debugger/unix_tools.ml @@ -36,7 +36,9 @@ let convert_address address = prerr_endline "The port number should be an integer"; failwith "Can't convert address"))) with Not_found -> - (PF_UNIX, ADDR_UNIX address) + match Sys.os_type with + "Win32" -> failwith "Unix sockets not supported" + | _ -> (PF_UNIX, ADDR_UNIX address) (*** Report a unix error. ***) let report_error = function diff --git a/myocamlbuild.ml b/myocamlbuild.ml index 5a61f2eb0..143f07fde 100644 --- a/myocamlbuild.ml +++ b/myocamlbuild.ml @@ -432,8 +432,8 @@ flag ["c"; "compile"; "otherlibs_num"] begin A"-I"; P"../otherlibs/num"] end;; flag ["c"; "compile"; "otherlibs_win32unix"] (A"-I../otherlibs/win32unix");; -flag [(* "ocaml" or "c"; *) "ocamlmklib"; "otherlibs_win32unix"] (S[A"-cclib"; Quote (syslib "wsock32")]);; -flag ["c"; "link"; "dll"; "otherlibs_win32unix"] (syslib "wsock32");; +flag [(* "ocaml" or "c"; *) "ocamlmklib"; "otherlibs_win32unix"] (S[A"-cclib"; Quote (syslib "ws2_32")]);; +flag ["c"; "link"; "dll"; "otherlibs_win32unix"] (syslib "ws2_32");; let flags = S[syslib "kernel32"; syslib "gdi32"; syslib "user32"] in flag ["c"; "ocamlmklib"; "otherlibs_win32graph"] (S[A"-cclib"; Quote flags]); flag ["c"; "link"; "dll"; "otherlibs_win32graph"] flags;; diff --git a/otherlibs/win32unix/Makefile.nt b/otherlibs/win32unix/Makefile.nt index 3ff9b7952..1fd635a7c 100644 --- a/otherlibs/win32unix/Makefile.nt +++ b/otherlibs/win32unix/Makefile.nt @@ -21,7 +21,8 @@ WIN_FILES = accept.c bind.c channels.c close.c \ mkdir.c open.c pipe.c read.c rename.c \ select.c sendrecv.c \ shutdown.c sleep.c socket.c sockopt.c startup.c stat.c \ - system.c unixsupport.c windir.c winwait.c write.c + system.c unixsupport.c windir.c winwait.c write.c \ + winlist.c winworker.c windbug.c # Files from the ../unix directory UNIX_FILES = access.c addrofstr.c chdir.c chmod.c cst2constr.c \ @@ -33,7 +34,7 @@ UNIX_FILES = access.c addrofstr.c chdir.c chmod.c cst2constr.c \ UNIX_CAML_FILES = unix.mli unixLabels.mli unixLabels.ml ALL_FILES=$(WIN_FILES) $(UNIX_FILES) -WSOCKLIB=$(call SYSLIB,wsock32) +WSOCKLIB=$(call SYSLIB,ws2_32) LIBNAME=unix COBJS=$(ALL_FILES:.c=.$(O)) diff --git a/otherlibs/win32unix/dllunix.dlib b/otherlibs/win32unix/dllunix.dlib index 01ffc59e6..e3ebf34e9 100644 --- a/otherlibs/win32unix/dllunix.dlib +++ b/otherlibs/win32unix/dllunix.dlib @@ -7,6 +7,7 @@ mkdir.d.o open.d.o pipe.d.o read.d.o rename.d.o select.d.o sendrecv.d.o shutdown.d.o sleep.d.o socket.d.o sockopt.d.o startup.d.o stat.d.o system.d.o unixsupport.d.o windir.d.o winwait.d.o write.d.o +winlist.d.o winworker.d.o windbug.d.o # Files from the ../unix directory access.d.o addrofstr.d.o chdir.d.o chmod.d.o cst2constr.d.o diff --git a/otherlibs/win32unix/libunix.clib b/otherlibs/win32unix/libunix.clib index 29b8d6e68..043dcf760 100644 --- a/otherlibs/win32unix/libunix.clib +++ b/otherlibs/win32unix/libunix.clib @@ -7,6 +7,7 @@ mkdir.o open.o pipe.o read.o rename.o select.o sendrecv.o shutdown.o sleep.o socket.o sockopt.o startup.o stat.o system.o unixsupport.o windir.o winwait.o write.o +winlist.o winworker.o windbug.o # Files from the ../unix directory access.o addrofstr.o chdir.o chmod.o cst2constr.o diff --git a/otherlibs/win32unix/select.c b/otherlibs/win32unix/select.c index 50a7da30b..1753a127e 100644 --- a/otherlibs/win32unix/select.c +++ b/otherlibs/win32unix/select.c @@ -17,85 +17,1031 @@ #include <alloc.h> #include <memory.h> #include <signals.h> +#include <winsock2.h> +#include <windows.h> #include "unixsupport.h" +#include "windbug.h" +#include "winworker.h" +#include "winlist.h" -static void fdlist_to_fdset(value fdlist, fd_set *fdset) +/* This constant define the maximum number of objects that + * can be handle by a SELECTDATA. + * It takes the following parameters into account: + * - limitation on number of objects is mostly due to limitation + * a WaitForMultipleObjects + * - there is always an event "hStop" to watch + * + * This lead to pick the following value as the biggest possible + * value + */ +#define MAXIMUM_SELECT_OBJECTS (MAXIMUM_WAIT_OBJECTS - 1) + +/* Manage set of handle */ +typedef struct _SELECTHANDLESET { + LPHANDLE lpHdl; + DWORD nMax; + DWORD nLast; +} SELECTHANDLESET; + +typedef SELECTHANDLESET *LPSELECTHANDLESET; + +void handle_set_init (LPSELECTHANDLESET hds, LPHANDLE lpHdl, DWORD max) +{ + DWORD i; + + hds->lpHdl = lpHdl; + hds->nMax = max; + hds->nLast = 0; + + /* Set to invalid value every entry of the handle */ + for (i = 0; i < hds->nMax; i++) + { + hds->lpHdl[i] = INVALID_HANDLE_VALUE; + }; +} + +void handle_set_add (LPSELECTHANDLESET hds, HANDLE hdl) +{ + LPSELECTHANDLESET res; + + if (hds->nLast < hds->nMax) + { + hds->lpHdl[hds->nLast] = hdl; + hds->nLast++; + } + + DBUG_PRINT("Adding handle %x to set %x", hdl, hds); +} + +BOOL handle_set_mem (LPSELECTHANDLESET hds, HANDLE hdl) +{ + BOOL res; + DWORD i; + + res = FALSE; + for (i = 0; !res && i < hds->nLast; i++) + { + res = (hds->lpHdl[i] == hdl); + } + + return res; +} + +void handle_set_reset (LPSELECTHANDLESET hds) +{ + DWORD i; + + for (i = 0; i < hds->nMax; i++) + { + hds->lpHdl[i] = INVALID_HANDLE_VALUE; + } + hds->nMax = 0; + hds->nLast = 0; + hds->lpHdl = NULL; +} + +/* Data structure for handling select */ + +typedef enum _SELECTHANDLETYPE { + SELECT_HANDLE_NONE = 0, + SELECT_HANDLE_DISK, + SELECT_HANDLE_CONSOLE, + SELECT_HANDLE_PIPE, + SELECT_HANDLE_SOCKET, +} SELECTHANDLETYPE; + +typedef enum _SELECTMODE { + SELECT_MODE_NONE = 0, + SELECT_MODE_READ, + SELECT_MODE_WRITE, + SELECT_MODE_EXCEPT, +} SELECTMODE; + +typedef enum _SELECTSTATE { + SELECT_STATE_NONE = 0, + SELECT_STATE_INITFAILED, + SELECT_STATE_ERROR, + SELECT_STATE_SIGNALED +} SELECTSTATE; + +typedef enum _SELECTTYPE { + SELECT_TYPE_NONE = 0, + SELECT_TYPE_STATIC, /* Result is known without running anything */ + SELECT_TYPE_CONSOLE_READ, /* Reading data on console */ + SELECT_TYPE_PIPE_READ, /* Reading data on pipe */ + SELECT_TYPE_SOCKET /* Classic select */ +} SELECTTYPE; + +/* Data structure for results */ +typedef struct _SELECTRESULT { + LIST lst; + SELECTMODE EMode; + LPVOID lpOrig; +} SELECTRESULT; + +typedef SELECTRESULT *LPSELECTRESULT; + +/* Data structure for query */ +typedef struct _SELECTQUERY { + LIST lst; + SELECTMODE EMode; + HANDLE hFileDescr; + LPVOID lpOrig; +} SELECTQUERY; + +typedef SELECTQUERY *LPSELECTQUERY; + +typedef struct _SELECTDATA { + LIST lst; + SELECTTYPE EType; + SELECTRESULT aResults[MAXIMUM_SELECT_OBJECTS]; + DWORD nResultsCount; + /* Data following are dedicated to APC like call, they + will be initialized if required. + */ + WORKERFUNC funcWorker; + SELECTQUERY aQueries[MAXIMUM_SELECT_OBJECTS]; + DWORD nQueriesCount; + SELECTSTATE EState; + DWORD nError; + LPWORKER lpWorker; +} SELECTDATA; + +typedef SELECTDATA *LPSELECTDATA; + +/* Get error status if associated condition is false */ +static BOOL check_error(LPSELECTDATA lpSelectData, BOOL bFailed) +{ + if (bFailed && lpSelectData->nError == 0) + { + lpSelectData->EState = SELECT_STATE_ERROR; + lpSelectData->nError = GetLastError(); + } + return bFailed; +} + +/* Create data associated with a select operation */ +LPSELECTDATA select_data_new (LPSELECTDATA lpSelectData, SELECTTYPE EType) +{ + /* Allocate the data structure */ + LPSELECTDATA res; + DWORD i; + + if (!HeapLock(GetProcessHeap())) + { + win32_maperr(GetLastError()); + uerror("select", Nothing); + } + res = (LPSELECTDATA)HeapAlloc(GetProcessHeap(), 0, sizeof(SELECTDATA)); + HeapUnlock(GetProcessHeap()); + + /* Init common data */ + list_init((LPLIST)res); + list_next_set((LPLIST)res, (LPLIST)lpSelectData); + res->EType = EType; + res->nResultsCount = 0; + + + /* Data following are dedicated to APC like call, they + will be initialized if required. For now they are set to + invalid values. + */ + res->funcWorker = NULL; + res->nQueriesCount = 0; + res->EState = SELECT_STATE_NONE; + res->nError = 0; + res->lpWorker = NULL; + + return res; +} + +/* Free select data */ +void select_data_free (LPSELECTDATA lpSelectData) +{ + DWORD i; + + DBUG_PRINT("Freeing data of %x", lpSelectData); + + /* Free APC related data, if they exists */ + if (lpSelectData->lpWorker != NULL) + { + worker_job_finish(lpSelectData->lpWorker); + lpSelectData->lpWorker = NULL; + }; + + /* Make sure results/queries cannot be accessed */ + lpSelectData->nResultsCount = 0; + lpSelectData->nQueriesCount = 0; + + if (!HeapLock(GetProcessHeap())) + { + win32_maperr(GetLastError()); + uerror("select_data_free", Nothing); + }; + HeapFree(GetProcessHeap(), 0, lpSelectData); + HeapUnlock(GetProcessHeap()); +} + +/* Add a result to select data, return zero if something goes wrong. */ +DWORD select_data_result_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, LPVOID lpOrig) +{ + DWORD res; + DWORD i; + + res = 0; + if (lpSelectData->nResultsCount < MAXIMUM_SELECT_OBJECTS) + { + i = lpSelectData->nResultsCount; + lpSelectData->aResults[i].EMode = EMode; + lpSelectData->aResults[i].lpOrig = lpOrig; + lpSelectData->nResultsCount++; + res = 1; + } + + return res; +} + +/* Add a query to select data, return zero if something goes wrong */ +DWORD select_data_query_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig) { - value l; - FD_ZERO(fdset); - for (l = fdlist; l != Val_int(0); l = Field(l, 1)) { - FD_SET(Socket_val(Field(l, 0)), fdset); + DWORD res; + DWORD i; + + res = 0; + if (lpSelectData->nQueriesCount < MAXIMUM_SELECT_OBJECTS) + { + i = lpSelectData->nQueriesCount; + lpSelectData->aQueries[i].EMode = EMode; + lpSelectData->aQueries[i].hFileDescr = hFileDescr; + lpSelectData->aQueries[i].lpOrig = lpOrig; + lpSelectData->nQueriesCount++; + res = 1; } + + return res; } -static value fdset_to_fdlist(value fdlist, fd_set *fdset) +/* Search for a job that has available query slots and that match provided type. + * If none is found, create a new one. Return the corresponding SELECTDATA, and + * update provided SELECTDATA head, if required. + */ +LPSELECTDATA select_data_job_search (LPSELECTDATA *lppSelectData, SELECTTYPE EType) { - value res = Val_int(0); - Begin_roots2(fdlist, res) - for (/*nothing*/; fdlist != Val_int(0); fdlist = Field(fdlist, 1)) { - value s = Field(fdlist, 0); - if (FD_ISSET(Socket_val(s), fdset)) { - value newres = alloc_small(2, 0); - Field(newres, 0) = s; - Field(newres, 1) = res; - res = newres; + LPSELECTDATA res; + + res = NULL; + + /* Search for job */ + DBUG_PRINT("Searching an available job for type %d", EType); + res = *lppSelectData; + while ( + res != NULL + && !( + res->EType == EType + && res->nQueriesCount < MAXIMUM_SELECT_OBJECTS + ) + ) + { + res = LIST_NEXT(LPSELECTDATA, res); + } + + /* No matching job found, create one */ + if (res == NULL) + { + DBUG_PRINT("No job for type %d found, create one", EType); + res = select_data_new(*lppSelectData, EType); + *lppSelectData = res; + } + + return res; +} + +/***********************/ +/* Console */ +/***********************/ + +void read_console_poll(HANDLE hStop, void *_data) +{ + HANDLE events[2]; + INPUT_RECORD record; + DWORD waitRes; + DWORD n; + LPSELECTDATA lpSelectData; + LPSELECTQUERY lpQuery; + + DBUG_PRINT("Waiting for data on console"); + + record; + waitRes = 0; + n = 0; + lpSelectData = (LPSELECTDATA)_data; + lpQuery = &(lpSelectData->aQueries[0]); + + events[0] = hStop; + events[1] = lpQuery->hFileDescr; + while (lpSelectData->EState == SELECT_STATE_NONE) + { + waitRes = WaitForMultipleObjects(2, events, FALSE, INFINITE); + if (waitRes == WAIT_OBJECT_0 || check_error(lpSelectData, waitRes == WAIT_FAILED)) + { + /* stop worker event or error */ + break; + } + /* console event */ + if (check_error(lpSelectData, PeekConsoleInput(lpQuery->hFileDescr, &record, 1, &n) == 0)) + { + break; + } + /* check for ASCII keypress only */ + if (record.EventType == KEY_EVENT && + record.Event.KeyEvent.bKeyDown && + record.Event.KeyEvent.uChar.AsciiChar != 0) + { + select_data_result_add(lpSelectData, lpQuery->EMode, lpQuery->lpOrig); + lpSelectData->EState = SELECT_STATE_SIGNALED; + break; + } + else + { + /* discard everything else and try again */ + if (check_error(lpSelectData, ReadConsoleInput(lpQuery->hFileDescr, &record, 1, &n) == 0)) + { + break; } } - End_roots(); + }; +} + +/* Add a function to monitor console input */ +LPSELECTDATA read_console_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig) +{ + LPSELECTDATA res; + + res = select_data_new(lpSelectData, SELECT_TYPE_CONSOLE_READ); + res->funcWorker = read_console_poll; + select_data_query_add(res, SELECT_MODE_READ, hFileDescr, lpOrig); + return res; } -CAMLprim value unix_select(value readfds, value writefds, value exceptfds, value timeout) +/***********************/ +/* Pipe */ +/***********************/ + +/* Monitor a pipe for input */ +void read_pipe_poll (HANDLE hStop, void *_data) +{ + DWORD event; + DWORD n; + LPSELECTQUERY iterQuery; + LPSELECTDATA lpSelectData; + DWORD i; + + /* Poll pipe */ + event = 0; + n = 0; + lpSelectData = (LPSELECTDATA)_data; + + DBUG_PRINT("Checking data pipe"); + while (lpSelectData->EState == SELECT_STATE_NONE) + { + for (i = 0; i < lpSelectData->nQueriesCount; i++) + { + iterQuery = &(lpSelectData->aQueries[i]); + if (check_error( + lpSelectData, + PeekNamedPipe( + iterQuery->hFileDescr, + NULL, + 0, + NULL, + &n, + NULL) == 0)) + { + break; + }; + + if (n > 0) + { + lpSelectData->EState = SELECT_STATE_SIGNALED; + select_data_result_add(lpSelectData, iterQuery->EMode, iterQuery->lpOrig); + }; + }; + + /* Alas, nothing except polling seems to work for pipes. + Check the state & stop_worker_event every 10 ms + */ + if (lpSelectData->EState == SELECT_STATE_NONE) + { + event = WaitForSingleObject(hStop, 10); + if (event == WAIT_OBJECT_0 || check_error(lpSelectData, event == WAIT_FAILED)) + { + break; + } + } + } + DBUG_PRINT("Finish checking data on pipe"); +} + +/* Add a function to monitor pipe input */ +LPSELECTDATA read_pipe_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig) +{ + LPSELECTDATA res; + LPSELECTDATA hd; + + hd = lpSelectData; + /* Polling pipe is a non blocking operation by default. This means that each + worker can handle many pipe. We begin to try to find a worker that is + polling pipe, but for which there is under the limit of pipe per worker. + */ + DBUG_PRINT("Searching an available worker handling pipe"); + res = select_data_job_search(&hd, SELECT_TYPE_PIPE_READ); + + /* Add a new pipe to poll */ + res->funcWorker = read_pipe_poll; + select_data_query_add(res, EMode, hFileDescr, lpOrig); + + return hd; +} + +/***********************/ +/* Socket */ +/***********************/ + +/* Monitor socket */ +void socket_poll (HANDLE hStop, void *_data) +{ + LPSELECTDATA lpSelectData; + LPSELECTQUERY iterQuery; + HANDLE aEvents[MAXIMUM_SELECT_OBJECTS]; + DWORD nEvents; + long maskEvents; + DWORD i; + u_long iMode; + + lpSelectData = (LPSELECTDATA)_data; + + for (nEvents = 0; nEvents < lpSelectData->nQueriesCount; nEvents++) + { + iterQuery = &(lpSelectData->aQueries[nEvents]); + aEvents[nEvents] = CreateEvent(NULL, TRUE, FALSE, NULL); + maskEvents = 0; + switch (iterQuery->EMode) + { + case SELECT_MODE_READ: + maskEvents = FD_READ | FD_ACCEPT | FD_CLOSE; + break; + case SELECT_MODE_WRITE: + maskEvents = FD_WRITE | FD_CONNECT | FD_CLOSE; + break; + case SELECT_MODE_EXCEPT: + maskEvents = FD_OOB; + break; + } + check_error(lpSelectData, + WSAEventSelect( + (SOCKET)(iterQuery->hFileDescr), + aEvents[nEvents], + maskEvents) == SOCKET_ERROR); + } + + /* Add stop event */ + aEvents[nEvents] = hStop; + nEvents++; + + if (lpSelectData->nError == 0) + { + check_error(lpSelectData, + WaitForMultipleObjects( + nEvents, + aEvents, + FALSE, + INFINITE) == WAIT_FAILED); + }; + + if (lpSelectData->nError == 0) + { + for (i = 0; i < lpSelectData->nQueriesCount; i++) + { + iterQuery = &(lpSelectData->aQueries[i]); + if (WaitForSingleObject(aEvents[i], 0) == WAIT_OBJECT_0) + { + DBUG_PRINT("Socket %d has pending events", (i - 1)); + if (iterQuery != NULL) + { + select_data_result_add(lpSelectData, iterQuery->EMode, iterQuery->lpOrig); + } + } + /* WSAEventSelect() automatically sets socket to nonblocking mode. + Restore the blocking one. */ + iMode = 0; + check_error(lpSelectData, + WSAEventSelect((SOCKET)(iterQuery->hFileDescr), aEvents[i], 0) != 0 || + ioctlsocket((SOCKET)(iterQuery->hFileDescr), FIONBIO, &iMode) != 0); + + CloseHandle(aEvents[i]); + aEvents[i] = INVALID_HANDLE_VALUE; + } + } +} + +/* Add a function to monitor socket */ +LPSELECTDATA socket_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig) +{ + LPSELECTDATA res; + LPSELECTDATA hd; + + hd = lpSelectData; + /* Polling socket can be done mulitple handle at the same time. You just + need one worker to use it. Try to find if there is already a worker + handling this kind of request. + */ + DBUG_PRINT("Scanning list of worker to find one that already handle socket"); + res = select_data_job_search(&hd, SELECT_TYPE_SOCKET); + + /* Add a new socket to poll */ + res->funcWorker = socket_poll; + DBUG_PRINT("Add socket %x to worker", hFileDescr); + select_data_query_add(res, EMode, hFileDescr, lpOrig); + DBUG_PRINT("Socket %x added", hFileDescr); + + return hd; +} + +/***********************/ +/* Static */ +/***********************/ + +/* Add a static result */ +LPSELECTDATA static_poll_add (LPSELECTDATA lpSelectData, SELECTMODE EMode, HANDLE hFileDescr, LPVOID lpOrig) { - fd_set read, write, except; - double tm; - struct timeval tv; - struct timeval * tvp; - int retcode; - value res; - value read_list = Val_unit, write_list = Val_unit, except_list = Val_unit; - DWORD err = 0; - - Begin_roots3 (readfds, writefds, exceptfds) - Begin_roots3 (read_list, write_list, except_list) - tm = Double_val(timeout); - if (readfds == Val_int(0) - && writefds == Val_int(0) - && exceptfds == Val_int(0)) { - if ( tm > 0.0 ) { - enter_blocking_section(); - Sleep( (int)(tm * 1000)); - leave_blocking_section(); + LPSELECTDATA res; + LPSELECTDATA hd; + + /* Look for an already initialized static element */ + hd = lpSelectData; + res = select_data_job_search(&hd, SELECT_TYPE_STATIC); + + /* Add a new query/result */ + select_data_query_add(res, EMode, hFileDescr, lpOrig); + select_data_result_add(res, EMode, lpOrig); + + return hd; +} + +/********************************/ +/* Generic select data handling */ +/********************************/ + +/* Guess handle type */ +static SELECTHANDLETYPE get_handle_type(value fd) +{ + DWORD mode; + SELECTHANDLETYPE res; + + CAMLparam1(fd); + + mode = 0; + res = SELECT_HANDLE_NONE; + + if (Descr_kind_val(fd) == KIND_SOCKET) + { + res = SELECT_HANDLE_SOCKET; + } + else + { + switch(GetFileType(Handle_val(fd))) + { + case FILE_TYPE_DISK: + res = SELECT_HANDLE_DISK; + break; + + case FILE_TYPE_CHAR: /* character file or a console */ + if (GetConsoleMode(Handle_val(fd), &mode) != 0) + { + res = SELECT_HANDLE_CONSOLE; + } + else + { + res = SELECT_HANDLE_NONE; + }; + break; + + case FILE_TYPE_PIPE: /* a named or an anonymous pipe (socket already handled) */ + res = SELECT_HANDLE_PIPE; + break; + }; + }; + + CAMLreturnT(SELECTHANDLETYPE, res); +} + +/* Choose what to do with given data */ +LPSELECTDATA select_data_dispatch (LPSELECTDATA lpSelectData, SELECTMODE EMode, value fd) +{ + LPSELECTDATA res; + HANDLE hFileDescr; + void *lpOrig; + struct sockaddr sa; + int sa_len; + BOOL alreadyAdded; + + CAMLparam1(fd); + + res = lpSelectData; + hFileDescr = Handle_val(fd); + lpOrig = (void *)fd; + sa_len = sizeof(sa); + alreadyAdded = FALSE; + + DBUG_PRINT("Begin dispatching handle %x", hFileDescr); + + DBUG_PRINT("Waiting for %d on handle %x", EMode, hFileDescr); + + /* There is only 2 way to have except mode: transmission of OOB data through + a socket TCP/IP and through a strange interaction with a TTY. + With windows, we only consider the TCP/IP except condition + */ + switch(get_handle_type(fd)) + { + case SELECT_HANDLE_DISK: + DBUG_PRINT("Handle %x is a disk handle", hFileDescr); + /* Disk is always ready in read/write operation */ + if (EMode == SELECT_MODE_READ || EMode == SELECT_MODE_WRITE) + { + res = static_poll_add(res, EMode, hFileDescr, lpOrig); + }; + break; + + case SELECT_HANDLE_CONSOLE: + DBUG_PRINT("Handle %x is a console handle", hFileDescr); + /* Console is always ready in write operation, need to check for read. */ + if (EMode == SELECT_MODE_READ) + { + res = read_console_poll_add(res, EMode, hFileDescr, lpOrig); } - read_list = write_list = except_list = Val_int(0); - } else { - fdlist_to_fdset(readfds, &read); - fdlist_to_fdset(writefds, &write); - fdlist_to_fdset(exceptfds, &except); - if (tm < 0.0) - tvp = (struct timeval *) NULL; - else { - tv.tv_sec = (int) tm; - tv.tv_usec = (int) (1e6 * (tm - (int) tm)); - tvp = &tv; + else if (EMode == SELECT_MODE_WRITE) + { + res = static_poll_add(res, EMode, hFileDescr, lpOrig); + }; + break; + + case SELECT_HANDLE_PIPE: + DBUG_PRINT("Handle %x is a pipe handle", hFileDescr); + /* Console is always ready in write operation, need to check for read. */ + if (EMode == SELECT_MODE_READ) + { + DBUG_PRINT("Need to check availability of data on pipe"); + res = read_pipe_poll_add(res, EMode, hFileDescr, lpOrig); } - enter_blocking_section(); - if (select(FD_SETSIZE, &read, &write, &except, tvp) == -1) - err = WSAGetLastError(); - leave_blocking_section(); - if (err) { - win32_maperr(err); - uerror("select", Nothing); + else if (EMode == SELECT_MODE_WRITE) + { + DBUG_PRINT("No need to check availability of data on pipe, write operation always possible"); + res = static_poll_add(res, EMode, hFileDescr, lpOrig); + }; + break; + + case SELECT_HANDLE_SOCKET: + DBUG_PRINT("Handle %x is a socket handle", hFileDescr); + if (getsockname((SOCKET)hFileDescr, &sa, &sa_len) == SOCKET_ERROR) + { + if (WSAGetLastError() == WSAEINVAL) + { + /* Socket is not bound */ + DBUG_PRINT("Socket is not connected"); + if (EMode == SELECT_MODE_WRITE || EMode == SELECT_MODE_READ) + { + res = static_poll_add(res, EMode, hFileDescr, lpOrig); + alreadyAdded = TRUE; + } + } } - read_list = fdset_to_fdlist(readfds, &read); - write_list = fdset_to_fdlist(writefds, &write); - except_list = fdset_to_fdlist(exceptfds, &except); - } - res = alloc_small(3, 0); - Field(res, 0) = read_list; - Field(res, 1) = write_list; - Field(res, 2) = except_list; - End_roots(); - End_roots(); - return res; + if (!alreadyAdded) + { + res = socket_poll_add(res, EMode, hFileDescr, lpOrig); + } + break; + + default: + DBUG_PRINT("Handle %x is unknown", hFileDescr); + caml_failwith("Unknown handle"); + break; + }; + + DBUG_PRINT("Finish dispatching handle %x", hFileDescr); + + CAMLreturnT(LPSELECTDATA, res); +} + +static DWORD caml_list_length (value lst) +{ + DWORD res; + + CAMLparam1 (lst); + CAMLlocal1 (l); + + for (res = 0, l = lst; l != Val_int(0); l = Field(l, 1), res++) + { } + + CAMLreturnT(DWORD, res); +} + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +CAMLprim value unix_select(value readfds, value writefds, value exceptfds, value timeout) +{ + /* Event associated to handle */ + DWORD nEventsCount; + DWORD nEventsMax; + HANDLE *lpEventsDone; + + /* Data for all handles */ + LPSELECTDATA lpSelectData; + LPSELECTDATA iterSelectData; + + /* Iterator for results */ + LPSELECTRESULT iterResult; + + /* Iterator */ + DWORD i; + + /* Error status */ + DWORD err; + + /* Time to wait */ + DWORD milliseconds; + + /* Wait return */ + DWORD waitRet; + + /* Set of handle */ + SELECTHANDLESET hds; + DWORD hdsMax; + LPHANDLE hdsData; + + /* Length of each list */ + DWORD readfds_len; + DWORD writefds_len; + DWORD exceptfds_len; + + CAMLparam4 (readfds, writefds, exceptfds, timeout); + CAMLlocal5 (read_list, write_list, except_list, res, l); + CAMLlocal1 (fd); + + DBUG_PRINT("in select"); + + nEventsCount = 0; + nEventsMax = 0; + lpEventsDone = NULL; + lpSelectData = NULL; + iterSelectData = NULL; + iterResult = NULL; + err = 0; + waitRet = 0; + readfds_len = caml_list_length(readfds); + writefds_len = caml_list_length(writefds); + exceptfds_len = caml_list_length(exceptfds); + hdsMax = MAX(readfds_len, MAX(writefds_len, exceptfds_len)); + + if (!HeapLock(GetProcessHeap())) + { + win32_maperr(GetLastError()); + uerror("select", Nothing); + } + hdsData = (HANDLE *)HeapAlloc( + GetProcessHeap(), + 0, + sizeof(HANDLE) * hdsMax); + HeapUnlock(GetProcessHeap()); + + if (Double_val(timeout) >= 0.0) + { + milliseconds = 1000 * Double_val(timeout); + DBUG_PRINT("Will wait %d ms", milliseconds); + } + else + { + milliseconds = INFINITE; + } + + + /* Create list of select data, based on the different list of fd to watch */ + DBUG_PRINT("Dispatch read fd"); + handle_set_init(&hds, hdsData, hdsMax); + for (l = readfds; l != Val_int(0); l = Field(l, 1)) + { + fd = Field(l, 0); + if (!handle_set_mem(&hds, Handle_val(fd))) + { + handle_set_add(&hds, Handle_val(fd)); + lpSelectData = select_data_dispatch(lpSelectData, SELECT_MODE_READ, fd); + } + else + { + DBUG_PRINT("Discarding handle %x which is already monitor for read", Handle_val(fd)); + } + } + handle_set_reset(&hds); + + DBUG_PRINT("Dispatch write fd"); + handle_set_init(&hds, hdsData, hdsMax); + for (l = writefds; l != Val_int(0); l = Field(l, 1)) + { + fd = Field(l, 0); + if (!handle_set_mem(&hds, Handle_val(fd))) + { + handle_set_add(&hds, Handle_val(fd)); + lpSelectData = select_data_dispatch(lpSelectData, SELECT_MODE_WRITE, fd); + } + else + { + DBUG_PRINT("Discarding handle %x which is already monitor for write", Handle_val(fd)); + } + } + handle_set_reset(&hds); + + DBUG_PRINT("Dispatch exceptional fd"); + handle_set_init(&hds, hdsData, hdsMax); + for (l = exceptfds; l != Val_int(0); l = Field(l, 1)) + { + fd = Field(l, 0); + if (!handle_set_mem(&hds, Handle_val(fd))) + { + handle_set_add(&hds, Handle_val(fd)); + lpSelectData = select_data_dispatch(lpSelectData, SELECT_MODE_EXCEPT, fd); + } + else + { + DBUG_PRINT("Discarding handle %x which is already monitor for exceptional", Handle_val(fd)); + } + } + handle_set_reset(&hds); + + /* Building the list of handle to wait for */ + DBUG_PRINT("Building events done array"); + nEventsMax = list_length((LPLIST)lpSelectData); + nEventsCount = 0; + if (!HeapLock(GetProcessHeap())) + { + win32_maperr(GetLastError()); + uerror("select", Nothing); + } + lpEventsDone = (HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE) * nEventsMax); + HeapUnlock(GetProcessHeap()); + + iterSelectData = lpSelectData; + while (iterSelectData != NULL) + { + /* Execute APC */ + if (iterSelectData->funcWorker != NULL) + { + iterSelectData->lpWorker = + worker_job_submit( + iterSelectData->funcWorker, + (void *)iterSelectData); + DBUG_PRINT("Job submitted to worker %x", iterSelectData->lpWorker); + lpEventsDone[nEventsCount] = worker_job_event_done(iterSelectData->lpWorker); + nEventsCount++; + }; + iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData); + }; + + DBUG_PRINT("Need to watch %d workers", nEventsCount); + + /* Processing select itself */ + enter_blocking_section(); + /* There are worker started, waiting to be monitored */ + if (nEventsCount > 0) + { + /* Waiting for event */ + if (err == 0) + { + DBUG_PRINT("Waiting for one select worker to be done"); + switch (WaitForMultipleObjects(nEventsCount, lpEventsDone, FALSE, milliseconds)) + { + case WAIT_FAILED: + err = GetLastError(); + break; + + case WAIT_TIMEOUT: + DBUG_PRINT("Select timeout"); + break; + + default: + DBUG_PRINT("One worker is done"); + break; + }; + } + + /* Ordering stop to every worker */ + DBUG_PRINT("Sending stop signal to every select workers"); + iterSelectData = lpSelectData; + while (iterSelectData != NULL) + { + if (iterSelectData->lpWorker != NULL) + { + worker_job_stop(iterSelectData->lpWorker); + }; + iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData); + }; + + DBUG_PRINT("Waiting for every select worker to be done"); + switch (WaitForMultipleObjects(nEventsCount, lpEventsDone, TRUE, INFINITE)) + { + case WAIT_FAILED: + err = GetLastError(); + break; + + default: + DBUG_PRINT("Every worker is done"); + break; + } + } + /* Nothing to monitor but some time to wait. */ + else + { + Sleep(milliseconds); + } + leave_blocking_section(); + + DBUG_PRINT("Error status: %d (0 is ok)", err); + /* Build results */ + if (err == 0) + { + DBUG_PRINT("Building result"); + read_list = Val_unit; + write_list = Val_unit; + except_list = Val_unit; + + iterSelectData = lpSelectData; + while (iterSelectData != NULL) + { + for (i = 0; i < iterSelectData->nResultsCount; i++) + { + iterResult = &(iterSelectData->aResults[i]); + l = alloc_small(2, 0); + Store_field(l, 0, (value)iterResult->lpOrig); + switch (iterResult->EMode) + { + case SELECT_MODE_READ: + Store_field(l, 1, read_list); + read_list = l; + break; + case SELECT_MODE_WRITE: + Store_field(l, 1, write_list); + write_list = l; + break; + case SELECT_MODE_EXCEPT: + Store_field(l, 1, except_list); + except_list = l; + break; + } + } + /* We try to only process the first error, bypass other errors */ + if (err == 0 && iterSelectData->EState == SELECT_STATE_ERROR) + { + err = iterSelectData->nError; + } + iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData); + } + } + + /* Free resources */ + DBUG_PRINT("Free selectdata resources"); + iterSelectData = lpSelectData; + while (iterSelectData != NULL) + { + lpSelectData = iterSelectData; + iterSelectData = LIST_NEXT(LPSELECTDATA, iterSelectData); + select_data_free(lpSelectData); + } + lpSelectData = NULL; + + /* Free allocated events/handle set array */ + DBUG_PRINT("Free local allocated resources"); + if (!HeapLock(GetProcessHeap())) + { + win32_maperr(GetLastError()); + uerror("select", Nothing); + } + HeapFree(GetProcessHeap(), 0, lpEventsDone); + HeapFree(GetProcessHeap(), 0, hdsData); + HeapUnlock(GetProcessHeap()); + + DBUG_PRINT("Raise error if required"); + if (err != 0) + { + win32_maperr(err); + uerror("select", Nothing); + } + + DBUG_PRINT("Build final result"); + res = alloc_small(3, 0); + Store_field(res, 0, read_list); + Store_field(res, 1, write_list); + Store_field(res, 2, except_list); + + DBUG_PRINT("out select"); + + CAMLreturn(res); } diff --git a/otherlibs/win32unix/startup.c b/otherlibs/win32unix/startup.c index ae584e569..bbf5fe1fe 100644 --- a/otherlibs/win32unix/startup.c +++ b/otherlibs/win32unix/startup.c @@ -16,6 +16,8 @@ #include <stdlib.h> #include <mlvalues.h> #include "unixsupport.h" +#include "winworker.h" +#include "windbug.h" value val_process_id; @@ -26,18 +28,27 @@ CAMLprim value win_startup(unit) int i; HANDLE h; + DBUG_INIT; + (void) WSAStartup(MAKEWORD(2, 0), &wsaData); DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &h, 0, TRUE, DUPLICATE_SAME_ACCESS); val_process_id = Val_int(h); + worker_init(); + return Val_unit; } CAMLprim value win_cleanup(unit) value unit; { + worker_cleanup(); + (void) WSACleanup(); + + DBUG_CLEANUP; + return Val_unit; } |