aboutsummaryrefslogtreecommitdiffstats
path: root/tools/debugger/pdb/server.ml
blob: 2d3a3c7c864de4ccca1a6dbf4fa427b4f42e46d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
(** server.ml
 *
 *  PDB server main loop
 *
 *  @author copyright (c) 2005 alex ho
 *  @see <www.cl.cam.ac.uk/netos/pdb> pervasive debugger
 *  @version 1
 *)

open Unix
open Buffer


(**
 * connection_t: The state for each connection.
 * buffer & length contains bytes that have been read from the sock
 * but not yet parsed / processed.
 *)
type connection_t =
{ 
          fd : file_descr;
  mutable buffer : string;
  mutable length : int;
}


(**
 * validate_checksum:  Compute and compare the checksum of a string
 * against the provided checksum using the gdb serial protocol algorithm.
 *
 *)
let validate_checksum command checksum =
  let c0 = ref 0 in
  for loop = 0 to (String.length command - 1) do
    c0 := !c0 + int_of_char(command.[loop]);
  done;
  if (String.length checksum) = 2 
  then
    let c1 = Util.int_of_hexchar(checksum.[1]) +
	     Util.int_of_hexchar(checksum.[0]) * 16 in
    (!c0 mod 256) = (c1 mod 256)
  else
    false
  

(**
 * process_input: Oh, joy!  Someone sent us a message.  Let's open the
 * envelope and see what they have to say.
 *
 * This function is a paradigm of inefficiency; it performs as many 
 * string copies as possible.
 *)
let process_input conn sock = 
  let max_buffer_size = 1024 in
  let in_string = String.create max_buffer_size in

  let length = read sock in_string 0 max_buffer_size in
  conn.buffer <- conn.buffer ^ (String.sub in_string 0 length);
  conn.length <- conn.length + length;
  let re = Str.regexp "[^\\$]*\\$\\([^#]*\\)#\\(..\\)" in

  begin
    try
      let break = String.index conn.buffer '\003' + 1 in
      print_endline (Printf.sprintf "{{%s}}" (String.escaped conn.buffer));

      (* discard everything seen before the ctrl-c *)
      conn.buffer <- String.sub conn.buffer break (conn.length - break);
      conn.length <- conn.length - break;

      (* pause the target *)
      PDB.pause (PDB.find_context sock);

      (* send a code back to the debugger *)
      Util.send_reply sock "S05"

    with
      Not_found -> ()
  end;

  (* with gdb this is unlikely to loop since you ack each packet *)
  while ( Str.string_match re conn.buffer 0 ) do
    let command = Str.matched_group 1 conn.buffer in
    let checksum = Str.matched_group 2 conn.buffer in
    let match_end = Str.group_end 2 in

    begin
      match validate_checksum command checksum with
      | true -> 
	  begin
	    Util.write_character sock '+';
	    try
	      let reply = Debugger.process_command command sock in
	      print_endline (Printf.sprintf "[%s] %s -> \"%s\"" 
			       (Util.get_connection_info sock)
			       (String.escaped command) 
			       (String.escaped reply));
	      Util.send_reply sock reply
	    with
	      Debugger.No_reply ->
		print_endline (Printf.sprintf "[%s] %s -> null" 
				 (Util.get_connection_info sock)
				 (String.escaped command))
	  end
      | false ->
	  Util.write_character sock '-';
    end;

    conn.buffer <- String.sub conn.buffer match_end (conn.length - match_end);
    conn.length <- conn.length - match_end;
  done;
  if length = 0 then raise End_of_file



(** main_server_loop.
 *
 *  connection_hash is a hash (duh!) with one connection_t for each
 *  open connection.
 * 
 *  in_list is a list of active sockets.  it also contains two 
 *  magic entries: server_sock for accepting new entries and 
 *  event_sock for Xen event channel asynchronous notifications.
 *)
let main_server_loop sockaddr =
  let connection_hash = Hashtbl.create 10
  in
  let process_socket svr_sock sockets sock =
    let (new_list, closed_list) = sockets in
    if sock == svr_sock
    then
      begin
	let (new_sock, caller) = accept sock in
	print_endline (Printf.sprintf "[%s] new connection from %s"
			              (Util.get_connection_info sock)
			              (Util.get_connection_info new_sock));
	Hashtbl.add connection_hash new_sock 
	            {fd=new_sock; buffer=""; length = 0};
	PDB.add_default_context new_sock;
	(new_sock :: new_list, closed_list)
      end
    else
      begin
	try
	  match PDB.find_context sock with
	  | PDB.Event_channel ->
	      print_endline (Printf.sprintf "[%s] event channel"
			                    (Util.get_connection_info sock));
	      Debugger.process_evtchn sock;
	      (new_list, closed_list)
	  | _ ->
	      let conn = Hashtbl.find connection_hash sock in
	      process_input conn sock;
	      (new_list, closed_list)
	with
	| Not_found -> 
	    print_endline "error: (main_svr_loop) context not found";
	    PDB.debug_contexts ();
	    raise Not_found
	| End_of_file -> 
	    print_endline (Printf.sprintf "[%s] close connection from %s"
  			                   (Util.get_connection_info sock)
			                   (Util.get_connection_info sock));
	    PDB.delete_context sock;
	    Hashtbl.remove connection_hash sock;
	    close sock;
	    (new_list, sock :: closed_list)
      end
  in
  let rec helper in_list server_sock =
  (*
   * List.iter (fun x->Printf.printf "{%s} " 
   *                                (Util.get_connection_info x)) in_list;   
   * Printf.printf "\n";
   *)
    let (rd_list, _, _) = select in_list [] [] (-1.0) in 
    let (new_list, closed_list) = List.fold_left (process_socket server_sock)
	                                         ([],[]) rd_list  in
    let merge_list = Util.list_remove (new_list @ in_list) closed_list  in
    helper merge_list server_sock
  in
  try
    let server_sock = socket (domain_of_sockaddr sockaddr) SOCK_STREAM 0 in
    setsockopt server_sock SO_REUSEADDR true;
    bind server_sock sockaddr;
    listen server_sock 2;

    PDB.open_debugger ();
    let event_sock = Evtchn.setup () in
    PDB.add_context event_sock "event channel" [];
    helper [server_sock; event_sock] server_sock
  with
  | Sys.Break ->
      print_endline "break: cleaning up";
      PDB.close_debugger ();
      Hashtbl.iter (fun sock conn -> close sock) connection_hash
  | Unix_error(e,err,param) -> 
      Printf.printf "unix error: [%s][%s][%s]\n" (error_message e) err param
  | Sys_error s -> Printf.printf "sys error: [%s]\n" s
  | Failure s -> Printf.printf "failure: [%s]\n" s
  | End_of_file -> Printf.printf "end of file\n"


let get_port () =
  if (Array.length Sys.argv) = 2 
  then
    int_of_string Sys.argv.(1)
  else
    begin
      print_endline (Printf.sprintf "syntax error: %s <port>" Sys.argv.(0));
      exit 1
    end


let main =
  let address = inet_addr_any in
  let port = get_port () in
  main_server_loop (ADDR_INET(address, port))