maint: added example command in release.sh
[barry.git] / src / j_server.cc
blobf7984e6569ca38ee7e15cf3b2c533203f3c3e968
1 ///
2 /// \file j_server.cc
3 /// Server protocol implementation
4 ///
6 /*
7 Copyright (C) 2009, Nicolas VIVIEN
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "j_server.h"
23 #include "protocol.h"
24 #include "data.h"
25 #include "endian.h"
26 #include "debug.h"
27 #include "j_message.h"
28 #include "protostructs.h"
29 #include "record-internal.h"
30 #include "error.h"
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <netdb.h>
39 #include <string.h>
40 #include <errno.h>
42 #include <sstream>
43 #include <algorithm>
45 using namespace std;
47 namespace Barry { namespace JDWP {
49 static void * acceptThread(void *data);
52 JDWServer::JDWServer(Barry::Mode::JVMDebug &device,
53 const char *address, int port)
54 : jvmdebug(&device)
55 , acceptfd(-1)
56 , sockfd(-1)
57 , address(address)
58 , port(port)
59 , loop(false)
60 , targetrunning(false)
61 , printConsoleMessage(0)
63 SearchDebugFile(debugFileList);
67 JDWServer::~JDWServer()
69 Stop();
73 void JDWServer::SetPasswordDevice(string password)
75 this->password = password;
79 void JDWServer::SetConsoleCallback(ConsoleCallbackType callback)
81 printConsoleMessage = callback;
84 static const char* h_strerror(int code)
86 // Codes and messages taken from the Linux gethostbyname(3) manpage
87 switch( code )
89 case HOST_NOT_FOUND:
90 return "HOST_NOT_FOUND: The specified host is unknown";
92 case NO_ADDRESS:
93 return "NO_ADDRESS: The requested name is valid but does not have an IP address";
95 case NO_RECOVERY:
96 return "NO_RECOVERY: A non-recoverable name server error occurred";
98 case TRY_AGAIN:
99 return "TRY_AGAIN: A temporary error occurred on an authoritative name server. Try again later.";
101 default:
102 return "Unknown network error code";
106 bool JDWServer::Start()
108 int rc;
110 struct hostent *hp;
111 struct sockaddr_in sad;
114 memset((char *) &sad, '\0', sizeof(struct sockaddr_in));
116 if (!address.size())
117 sad.sin_addr.s_addr = INADDR_ANY;
118 else {
119 sad.sin_addr.s_addr = inet_addr(address.c_str());
121 if (sad.sin_addr.s_addr == INADDR_NONE) {
122 hp = gethostbyname(address.c_str());
124 if (hp == NULL) {
125 std::ostringstream oss;
126 oss << "JDWServer::Start: " << h_errno << h_strerror(h_errno);
127 throw Barry::Error(oss.str());
130 memcpy((char*) &sad.sin_addr, (char*) hp->h_addr, (size_t) hp->h_length);
134 sad.sin_family = AF_INET;
135 sad.sin_port = htons((short) (port & 0xFFFF));
137 // Open socket
138 sockfd = socket(sad.sin_family, SOCK_STREAM, 0);
140 if (sockfd < 0) {
141 throw Barry::ErrnoError("JDWServer::Start: Cannot open socket.", errno);
144 // Bind
145 rc = bind(sockfd, (struct sockaddr *) &sad, sizeof(sad));
147 if (rc < 0) {
148 int code = errno;
150 close(sockfd);
151 sockfd = -1;
153 throw Barry::ErrnoError("JDWServer::Start: Cannot bind socket", code);
156 // Listen
157 if (listen(sockfd, SOMAXCONN) < 0) {
158 int code = errno;
160 close(sockfd);
161 sockfd = -1;
163 throw Barry::ErrnoError("JDWServer::Start: Cannot listen on socket", code);
166 handler.reset(new Thread(sockfd, acceptThread, (void*) this));
168 return true;
172 static void * acceptThread(void *data)
174 JDWServer *s;
176 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
178 s = (JDWServer *) data;
180 while (1) {
181 if( s->AcceptConnection() &&
182 s->AttachToDevice() &&
183 s->InitVisibleClassList() &&
184 s->Hello() )
186 s->Run();
187 s->DetachFromDevice();
191 return NULL;
195 // Returns true if a new connection was accepted and established
196 bool JDWServer::AcceptConnection()
198 struct sockaddr_in addr;
199 struct sockaddr *sa = (struct sockaddr*) &addr;
200 socklen_t addrlen = sizeof(addr);
202 acceptfd = accept(sockfd, sa, &addrlen);
203 if( acceptfd < 0 )
204 return false;
206 fcntl(acceptfd, F_SETFL, O_NONBLOCK);
207 return true;
211 bool JDWServer::AttachToDevice()
213 targetrunning = false;
215 jvmdebug->Open(password.c_str());
216 jvmdebug->Attach();
218 jvmdebug->Unknown01();
219 jvmdebug->Unknown02();
220 jvmdebug->Unknown03();
221 jvmdebug->Unknown04();
222 jvmdebug->Unknown05();
224 jvmdebug->GetModulesList(modulesList);
225 dout(modulesList);
227 // Check debug info for each modules
228 JVMModulesList::const_iterator b = modulesList.begin();
229 for ( ; b != modulesList.end(); b++) {
230 JDG::CodInfo codInfo;
232 const JVMModulesEntry &entry = *b;
234 bool ret = LoadDebugInfo(debugFileList, entry.UniqueID, entry.Name, codInfo);
236 if (ret == true) {
237 appList[entry.UniqueID].Load(codInfo);
239 else {
240 dout("No debug information found for '" << entry.Name);
241 dout("' (" << hex << setfill('0') << setw(8) << entry.UniqueID << ")." << endl)
245 return true;
249 void JDWServer::DetachFromDevice()
251 jvmdebug->Detach();
252 jvmdebug->Close();
256 #define JDWP_HELLO_STRING "JDWP-Handshake"
260 bool JDWServer::Hello()
262 bool ret;
264 Barry::Data response;
266 const size_t len = strlen(JDWP_HELLO_STRING);
268 JDWMessage msg(acceptfd);
270 do {
271 ret = msg.Receive(response);
273 while (!ret);
275 size_t size = response.GetSize();
276 char *str = (char *) response.GetBuffer();
278 if (size != len)
279 return false;
281 if (!strncmp(str, JDWP_HELLO_STRING, len)) {
282 Data command(JDWP_HELLO_STRING, len);
284 msg.Send(command);
286 return true;
289 return false;
293 void JDWServer::Run()
295 string str;
296 JDWMessage msg(acceptfd);
298 Barry::Data command;
300 MAKE_JDWPPACKET(rpack, command);
302 loop = true;
304 while (loop) {
305 if (targetrunning) {
306 // Read JDWP message from device
307 int value = jvmdebug->GetConsoleMessage(str);
309 if (value < 0) {
310 bool ret;
311 int status;
313 ret = jvmdebug->GetStatus(status);
315 while (!ret) {
316 // Read JDB message from host
317 msg.Receive(command);
319 if (command.GetSize() > 0) {
320 // Convert to packet
321 rpack = (const Barry::Protocol::JDWP::Packet *) command.GetData();
323 if (command.GetSize() != be_btohl(rpack->length)) {
324 dout("Packet size error !!!" << endl);
326 // TODO : add throw exception
328 continue;
331 CommandsetProcess(command);
333 break;
335 else
336 ret = jvmdebug->WaitStatus(status);
339 else {
340 if (printConsoleMessage != NULL)
341 printConsoleMessage(str);
344 else {
345 // Read JDB message from host
346 msg.Receive(command);
348 if (command.GetSize() > 0) {
349 // Convert to packet
350 rpack = (const Barry::Protocol::JDWP::Packet *) command.GetData();
352 if (command.GetSize() != be_btohl(rpack->length)) {
353 dout("Packet size error !!!" << endl);
355 // TODO : add throw exception
357 continue;
360 CommandsetProcess(command);
363 usleep(50);
369 bool JDWServer::Stop()
371 if( handler.get() ) {
372 handler->Dispose();
373 handler.reset();
376 if( sockfd >= 0 ) {
377 close(sockfd);
378 sockfd = -1;
381 if( acceptfd >= 0 ) {
382 close(acceptfd);
383 acceptfd = -1;
386 return true;
390 bool JDWServer::InitVisibleClassList()
392 int index;
394 // Skip the cell '0'
395 // it's very ugly, but I want use an index started at '1' inside of '0'
396 // JDB works from '1' :(
397 JDG::ClassEntry e;
398 visibleClassList.push_back(e);
400 // Count and index the class (start to '1')
401 index = 1;
402 JDWAppList::iterator it;
404 for (it = appList.begin(); it != appList.end(); it++) {
405 JDWAppInfo &appInfo = it->second;
406 JDG::ClassList &list = appInfo.classList;
408 JDG::ClassList::iterator b;
410 for (b = list.begin(); b != list.end(); b++) {
411 // FIXME
412 // I don't from class field, we have to filter the class view by JDB
413 // if ((b->type != 0x824) && (b->type != 0x64)) {
414 if (b->id == 0xffffffff) {
415 b->index = -1;
417 continue;
420 b->index = index;
422 visibleClassList.push_back(*b);
424 index++;
428 visibleClassList.CreateDefaultEntries();
430 return true;
434 void JDWServer::CommandsetProcess(Data &cmd)
436 MAKE_JDWPPACKET(rpack, cmd);
438 switch (rpack->u.command.commandset) {
439 case JDWP_CMDSET_VIRTUALMACHINE:
440 CommandsetVirtualMachineProcess(cmd);
441 break;
443 case JDWP_CMDSET_REFERECENTYPE:
444 break;
446 case JDWP_CMDSET_CLASSTYPE:
447 break;
449 case JDWP_CMDSET_ARRAYTYPE:
450 break;
452 case JDWP_CMDSET_INTERFACETYPE:
453 break;
455 case JDWP_CMDSET_METHOD:
456 break;
458 case JDWP_CMDSET_FIELD:
459 break;
461 case JDWP_CMDSET_OBJECTREFERENCE:
462 break;
464 case JDWP_CMDSET_STRINGREFERENCE:
465 break;
467 case JDWP_CMDSET_THREADREFERENCE:
468 break;
470 case JDWP_CMDSET_THREADGROUPREFERENCE:
471 break;
473 case JDWP_CMDSET_ARRAYREFERENCE:
474 break;
476 case JDWP_CMDSET_CLASSLOADERREFERENCE:
477 break;
479 case JDWP_CMDSET_EVENTREQUEST:
480 CommandsetEventRequestProcess(cmd);
481 break;
483 case JDWP_CMDSET_STACKFRAME:
484 break;
486 case JDWP_CMDSET_CLASSOBJECTREFERENCE:
487 break;
489 case JDWP_CMDSET_EVENT:
490 break;
492 default:
493 // TODO : add exception (or alert)
494 dout("Commandset unknown !!!" << endl);
499 void JDWServer::CommandsetVirtualMachineProcess(Data &cmd)
501 MAKE_JDWPPACKET(rpack, cmd);
503 switch (rpack->u.command.command) {
504 case JDWP_CMD_VERSION:
505 CommandVersion(cmd);
506 break;
508 case JDWP_CMD_ALLCLASSES:
509 CommandAllClasses(cmd);
510 break;
512 case JDWP_CMD_ALLTHREADS:
513 CommandAllThreads(cmd);
514 break;
516 case JDWP_CMD_DISPOSE:
517 loop = false;
518 targetrunning = false;
519 close(acceptfd);
520 acceptfd = -1;
521 break;
523 case JDWP_CMD_IDSIZES:
524 CommandIdSizes(cmd);
525 break;
527 case JDWP_CMD_SUSPEND:
528 CommandSuspend(cmd);
529 targetrunning = false;
530 break;
532 case JDWP_CMD_RESUME:
533 CommandResume(cmd);
534 targetrunning = true;
535 break;
537 case JDWP_CMD_CLASSPATHS:
538 CommandClassPaths(cmd);
539 break;
544 void JDWServer::CommandsetEventRequestProcess(Data &cmd)
546 MAKE_JDWPPACKET(rpack, cmd);
548 switch (rpack->u.command.command) {
549 case JDWP_CMD_SET:
550 CommandSet(cmd);
551 break;
556 void JDWServer::CommandVersion(Data &cmd)
558 JDWMessage msg(acceptfd);
560 // Build packet data
561 Data response;
563 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
565 AddJDWString(response, offset, string("RIM JVM"));
566 AddJDWInt(response, offset, be_htobl(1));
567 AddJDWInt(response, offset, be_htobl(4));
568 AddJDWString(response, offset, string("1.4"));
569 AddJDWString(response, offset, string("RIM JVM"));
571 response.ReleaseBuffer(offset);
574 size_t total_size = response.GetSize();
576 // Fill in the header values
577 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
578 Barry::Protocol::JDWP::Packet &packet = *cpack;
581 MAKE_JDWPPACKET(rpack, cmd);
583 packet.length = be_htobl(total_size);
584 packet.id = rpack->id;
585 packet.flags = 0x80;
586 packet.u.response.errorcode = be_htobs(0);
588 response.ReleaseBuffer(total_size);
589 msg.Send(response);
593 void JDWServer::CommandAllClasses(Data &cmd)
595 size_t i;
596 int size;
598 JDWMessage msg(acceptfd);
600 // Build packet data
601 Data response;
603 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
605 // Size of known class list
606 size = visibleClassList.size() - 1;
608 AddJDWInt(response, offset, be_htobl(size));
610 // Then, write the list of known class
611 for (i=1; i<visibleClassList.size(); i++) {
612 string str = visibleClassList[i].GetFullClassName();
614 str = "L" + str + ";";
615 replace_if(str.begin(), str.end(), bind2nd(equal_to<char>(),'.'), '/');
617 AddJDWByte(response, offset, 0x01);
618 AddJDWInt(response, offset, i); // Should be equal to visibleClassList[i].index
619 AddJDWString(response, offset, str);
620 AddJDWInt(response, offset, be_htobl(0x04));
623 response.ReleaseBuffer(offset);
626 size_t total_size = response.GetSize();
628 // Fill in the header values
629 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
630 Barry::Protocol::JDWP::Packet &packet = *cpack;
633 MAKE_JDWPPACKET(rpack, cmd);
635 packet.length = be_htobl(total_size);
636 packet.id = rpack->id;
637 packet.flags = 0x80;
638 packet.u.response.errorcode = be_htobs(0);
640 response.ReleaseBuffer(total_size);
641 msg.Send(response);
645 void JDWServer::CommandAllThreads(Data &cmd)
647 JDWMessage msg(acceptfd);
649 // Get threads list from device
650 JVMThreadsList list;
651 jvmdebug->GetThreadsList(list);
652 dout(list);
654 // Build packet data
655 Data response;
657 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
659 // Indicate the number of element
660 AddJDWInt(response, offset, be_htobl(list.size()));
662 // Send all threads ID
663 JVMThreadsList::const_iterator b = list.begin();
664 for( ; b != list.end(); b++ ) {
665 const JVMThreadsEntry &entry = *b;
667 AddJDWInt(response, offset, be_htobl(entry.Id));
670 response.ReleaseBuffer(offset);
673 size_t total_size = response.GetSize();
675 // Fill in the header values
676 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
677 Barry::Protocol::JDWP::Packet &packet = *cpack;
680 MAKE_JDWPPACKET(rpack, cmd);
682 packet.length = be_htobl(total_size);
683 packet.id = rpack->id;
684 packet.flags = 0x80;
685 packet.u.response.errorcode = be_htobs(0);
687 response.ReleaseBuffer(total_size);
688 msg.Send(response);
692 void JDWServer::CommandIdSizes(Data &cmd)
694 JDWMessage msg(acceptfd);
696 MAKE_JDWPPACKET(rpack, cmd);
698 size_t size;
700 Barry::Protocol::JDWP::Packet packet;
702 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE
703 + JDWP_PACKETVIRTUALMACHINEIDSIZES_DATA_SIZE;
705 packet.length = be_htobl(size);
706 packet.id = rpack->id;
707 packet.flags = 0x80;
708 packet.u.response.errorcode = be_htobs(0);
709 packet.u.response.u.virtualMachine.u.IDSizes.fieldIDSize = be_htobl(0x04);
710 packet.u.response.u.virtualMachine.u.IDSizes.methodIDSize = be_htobl(0x04);
711 packet.u.response.u.virtualMachine.u.IDSizes.objectIDSize = be_htobl(0x04);
712 packet.u.response.u.virtualMachine.u.IDSizes.referenceTypeIDSize = be_htobl(0x04);
713 packet.u.response.u.virtualMachine.u.IDSizes.frameIDSize = be_htobl(0x04);
715 Data response(&packet, size);
717 msg.Send(response);
721 void JDWServer::CommandSuspend(Data &cmd)
723 JDWMessage msg(acceptfd);
726 // Suspend device
727 jvmdebug->Stop();
729 // Notify debugger
730 MAKE_JDWPPACKET(rpack, cmd);
732 size_t size;
734 Barry::Protocol::JDWP::Packet packet;
736 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
738 packet.length = be_htobl(size);
739 packet.id = rpack->id;
740 packet.flags = 0x80;
741 packet.u.response.errorcode = be_htobs(0);
743 Data response(&packet, size);
745 msg.Send(response);
749 void JDWServer::CommandResume(Data &cmd)
751 JDWMessage msg(acceptfd);
754 // Resume device
755 jvmdebug->Unknown06();
756 jvmdebug->Unknown07();
757 jvmdebug->Unknown08();
758 jvmdebug->Unknown09();
759 jvmdebug->Unknown10();
760 jvmdebug->Go();
762 // Notify debugger
763 MAKE_JDWPPACKET(rpack, cmd);
765 size_t size;
767 Barry::Protocol::JDWP::Packet packet;
769 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
771 packet.length = be_htobl(size);
772 packet.id = rpack->id;
773 packet.flags = 0x80;
774 packet.u.response.errorcode = be_htobs(0);
776 Data response(&packet, size);
778 msg.Send(response);
782 void JDWServer::CommandClassPaths(Data &cmd)
784 JDWMessage msg(acceptfd);
786 // Build packet data
787 Data response;
789 size_t offset = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE;
791 AddJDWString(response, offset, string(""));
792 AddJDWInt(response, offset, be_htobl(0));
793 AddJDWInt(response, offset, be_htobl(0));
795 response.ReleaseBuffer(offset);
798 size_t total_size = response.GetSize();
800 // Fill in the header values
801 MAKE_JDWPPACKETPTR_BUF(cpack, response.GetBuffer(total_size));
802 Barry::Protocol::JDWP::Packet &packet = *cpack;
805 MAKE_JDWPPACKET(rpack, cmd);
807 packet.length = be_htobl(total_size);
808 packet.id = rpack->id;
809 packet.flags = 0x80;
810 packet.u.response.errorcode = be_htobs(0);
812 response.ReleaseBuffer(total_size);
813 msg.Send(response);
818 void JDWServer::CommandSet(Data &cmd)
820 static int value = 2;
822 JDWMessage msg(acceptfd);
824 MAKE_JDWPPACKET(rpack, cmd);
826 size_t size;
828 Barry::Protocol::JDWP::Packet packet;
830 size = JDWP_PACKET_HEADER_SIZE + JDWP_RESPONSE_HEADER_SIZE + sizeof(uint32_t);
832 packet.length = be_htobl(size);
833 packet.id = rpack->id;
834 packet.flags = 0x80;
835 packet.u.response.errorcode = be_htobs(0);
836 packet.u.response.u.value = be_htobl(value);
838 Data response(&packet, size);
840 msg.Send(response);
842 value++;
846 }} // namespace Barry::JDWP