Because of their distributed nature, RPC applications can be very difficult to debug. One easy way to test and debug an RPC application with, say, gdb , is to link the client and server programs without their rpcgen stubs. To do this, comment out the RPC reference in the client program. If the -C option was passed to rpcgen , then you must adjust the name of the function call appropriately (i.e., add the _svc suffix). In addition, you may need to cast the function call argument with the client reference to the correct type (i.e., struct svc_req * ). Incorporating these changes with preprocessor directives, our hello_client.c file now would be as shown in Figure 9.17.
Figure 9.17 A "debug ready" version of hello_client.c .
File : hello_client_gdb.c /* The CLIENT program: hello_client.c This will be the client code executed by the local client process. */ + #include #include "hello.h" /* Generated by rpcgen from hello.x */ int main(int argc, char *argv[]) { . . . /* SAME AS LINES 9-20 in hello_client.c */ /* Generate the client handle to call the server */ #ifndef DEBUG + if ((client=clnt_create(server, DISPLAY_PRG, DISPLAY_VER, "tcp")) == (CLIENT *) NULL) { clnt_pcreateerror(server); exit(2); } 30 printf("client : calling function. "); return_value = print_hello_1((void *) &filler, client); #else printf("client : calling function. "); return_value = print_hello_1_ svc ((void *) &filler, ( struct svc_req * )client); + #endif if (*return_value) printf("client : Mission accomplished "); else printf("client : Unable to display message "); 40 return 0; }
We would compile this modified version with the command sequence shown in Figure 9.18. As none of the network libraries are referenced, the libnsl library does not need to be linked (for most versions of gcc , this is not a concern). The compiler is passed the -g flag (to generate the symbol table information for gdb ) and -DDEBUG is specified to define the DEBUG constant the preprocessor will test.
Figure 9.18 Debugging the clientserver application with gdb .
linux$ gcc -DDEBUG -g hello_client_gdb.c hello_server.c <-- 1 linux$ gdb -q a.out (gdb) list 25,35 25 if ((client=clnt_create(server, DISPLAY_PRG, 26 DISPLAY_VER, "tcp")) == (CLIENT *) NULL) { 27 clnt_pcreateerror(server); 28 exit(2); 29 } 30 printf("client : calling function. "); 31 return_value = print_hello_1((void *) &filler, client); 32 #else 33 printf("client : calling function. "); 34 return_value=print_hello_1_svc((void *) &filler,(struct svc_req *)client); 35 #endif (gdb) break 34 <-- 2 Breakpoint 1 at 0x804853f: file hello_client_gdb.c, line 34. (gdb) run kahuna Starting program: /home/faculty/gray/revision/09/hello_files/a.out kahuna client : calling function. Breakpoint 1, main (argc=2, argv=0xbffffc34) at hello_client_gdb.c:34 34 return_value=print_hello_1_svc((void *)&filler,(struct svc_req *)client); (gdb) step <-- 3 print_hello_1_svc (filler=0xbffffbbc, req=0x80497ec) at hello_server.c:10 10 ok = printf("server : Hello, world. "); (gdb) list <-- 4 5 #include 6 #include "hello.h" /* is generated by rpcgen from hello.x */ 7 int * 8 print_hello_1_svc(void * filler, struct svc_req * req) { 9 static int ok; 10 ok = printf("server : Hello, world. "); 11 return (&ok); 12 } (gdb) quit The program is running. Exit anyway? (y or n) y
(1) Compile with gcc . Define the DEBUG constant and generate the symbol table information.
(2) Set a break point at line 34 in the client program.
(3) Step into what was formerly the remote procedure.
(4) This is now the code for the server.
Programs and Processes
Processing Environment
Using Processes
Primitive Communications
Pipes
Message Queues
Semaphores
Shared Memory
Remote Procedure Calls
Sockets
Threads
Appendix A. Using Linux Manual Pages
Appendix B. UNIX Error Messages
Appendix C. RPC Syntax Diagrams
Appendix D. Profiling Programs