3 /// Server protocol implementation
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.
27 #include "j_message.h"
28 #include "protostructs.h"
29 #include "record-internal.h"
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
47 namespace Barry
{ namespace JDWP
{
49 static void * acceptThread(Barry::Thread::CallbackData
*data
);
52 JDWServer::JDWServer(Barry::Mode::JVMDebug
&device
,
53 const char *address
, int port
)
60 , targetrunning(false)
61 , printConsoleMessage(0)
63 SearchDebugFile(debugFileList
);
67 JDWServer::~JDWServer()
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
90 return "HOST_NOT_FOUND: The specified host is unknown";
93 return "NO_ADDRESS: The requested name is valid but does not have an IP address";
96 return "NO_RECOVERY: A non-recoverable name server error occurred";
99 return "TRY_AGAIN: A temporary error occurred on an authoritative name server. Try again later.";
102 return "Unknown network error code";
106 bool JDWServer::Start()
111 struct sockaddr_in sad
;
114 memset((char *) &sad
, '\0', sizeof(struct sockaddr_in
));
117 sad
.sin_addr
.s_addr
= INADDR_ANY
;
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());
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));
138 sockfd
= socket(sad
.sin_family
, SOCK_STREAM
, 0);
141 throw Barry::ErrnoError("JDWServer::Start: Cannot open socket.", errno
);
145 rc
= bind(sockfd
, (struct sockaddr
*) &sad
, sizeof(sad
));
153 throw Barry::ErrnoError("JDWServer::Start: Cannot bind socket", code
);
157 if (listen(sockfd
, SOMAXCONN
) < 0) {
163 throw Barry::ErrnoError("JDWServer::Start: Cannot listen on socket", code
);
166 handler
.reset(new Thread(sockfd
, acceptThread
, (void*) this));
172 static void * acceptThread(Barry::Thread::CallbackData
*data
)
174 JDWServer
*s
= (JDWServer
*) data
->userdata
;
176 while( !data
->stopflag
) {
177 if( s
->AcceptConnection() &&
178 s
->AttachToDevice() &&
179 s
->InitVisibleClassList() &&
182 s
->Run(data
->stopflag
);
183 s
->DetachFromDevice();
191 // Returns true if a new connection was accepted and established
192 bool JDWServer::AcceptConnection()
194 struct sockaddr_in addr
;
195 struct sockaddr
*sa
= (struct sockaddr
*) &addr
;
196 socklen_t addrlen
= sizeof(addr
);
198 acceptfd
= accept(sockfd
, sa
, &addrlen
);
202 fcntl(acceptfd
, F_SETFL
, O_NONBLOCK
);
207 bool JDWServer::AttachToDevice()
209 targetrunning
= false;
211 jvmdebug
->Open(password
.c_str());
214 jvmdebug
->Unknown01();
215 jvmdebug
->Unknown02();
216 jvmdebug
->Unknown03();
217 jvmdebug
->Unknown04();
218 jvmdebug
->Unknown05();
220 jvmdebug
->GetModulesList(modulesList
);
223 // Check debug info for each modules
224 JVMModulesList::const_iterator b
= modulesList
.begin();
225 for ( ; b
!= modulesList
.end(); b
++) {
226 JDG::CodInfo codInfo
;
228 const JVMModulesEntry
&entry
= *b
;
230 bool ret
= LoadDebugInfo(debugFileList
, entry
.UniqueID
, entry
.Name
, codInfo
);
233 appList
[entry
.UniqueID
].Load(codInfo
);
236 dout("No debug information found for '" << entry
.Name
);
237 dout("' (" << hex
<< setfill('0') << setw(8) << entry
.UniqueID
<< ")." << endl
)
245 void JDWServer::DetachFromDevice()
252 #define JDWP_HELLO_STRING "JDWP-Handshake"
256 bool JDWServer::Hello()
260 Barry::Data response
;
262 const size_t len
= strlen(JDWP_HELLO_STRING
);
264 JDWMessage
msg(acceptfd
);
267 ret
= msg
.Receive(response
);
271 size_t size
= response
.GetSize();
272 char *str
= (char *) response
.GetBuffer();
277 if (!strncmp(str
, JDWP_HELLO_STRING
, len
)) {
278 Data
command(JDWP_HELLO_STRING
, len
);
289 void JDWServer::Run(volatile bool &stopflag
)
292 JDWMessage
msg(acceptfd
);
296 MAKE_JDWPPACKET(rpack
, command
);
300 while( loop
&& !stopflag
) {
302 // Read JDWP message from device
303 int value
= jvmdebug
->GetConsoleMessage(str
);
309 ret
= jvmdebug
->GetStatus(status
);
312 // Read JDB message from host
313 msg
.Receive(command
);
315 if (command
.GetSize() > 0) {
317 rpack
= (const Barry::Protocol::JDWP::Packet
*) command
.GetData();
319 if (command
.GetSize() != be_btohl(rpack
->length
)) {
320 dout("Packet size error !!!" << endl
);
322 // TODO : add throw exception
327 CommandsetProcess(command
);
332 ret
= jvmdebug
->WaitStatus(status
);
336 if (printConsoleMessage
!= NULL
)
337 printConsoleMessage(str
);
341 // Read JDB message from host
342 msg
.Receive(command
);
344 if (command
.GetSize() > 0) {
346 rpack
= (const Barry::Protocol::JDWP::Packet
*) command
.GetData();
348 if (command
.GetSize() != be_btohl(rpack
->length
)) {
349 dout("Packet size error !!!" << endl
);
351 // TODO : add throw exception
356 CommandsetProcess(command
);
365 bool JDWServer::Stop()
367 if( handler
.get() ) {
377 if( acceptfd
>= 0 ) {
386 bool JDWServer::InitVisibleClassList()
391 // it's very ugly, but I want use an index started at '1' inside of '0'
392 // JDB works from '1' :(
394 visibleClassList
.push_back(e
);
396 // Count and index the class (start to '1')
398 JDWAppList::iterator it
;
400 for (it
= appList
.begin(); it
!= appList
.end(); it
++) {
401 JDWAppInfo
&appInfo
= it
->second
;
402 JDG::ClassList
&list
= appInfo
.classList
;
404 JDG::ClassList::iterator b
;
406 for (b
= list
.begin(); b
!= list
.end(); b
++) {
408 // I don't from class field, we have to filter the class view by JDB
409 // if ((b->type != 0x824) && (b->type != 0x64)) {
410 if (b
->id
== 0xffffffff) {
418 visibleClassList
.push_back(*b
);
424 visibleClassList
.CreateDefaultEntries();
430 void JDWServer::CommandsetProcess(Data
&cmd
)
432 MAKE_JDWPPACKET(rpack
, cmd
);
434 switch (rpack
->u
.command
.commandset
) {
435 case JDWP_CMDSET_VIRTUALMACHINE
:
436 CommandsetVirtualMachineProcess(cmd
);
439 case JDWP_CMDSET_REFERECENTYPE
:
442 case JDWP_CMDSET_CLASSTYPE
:
445 case JDWP_CMDSET_ARRAYTYPE
:
448 case JDWP_CMDSET_INTERFACETYPE
:
451 case JDWP_CMDSET_METHOD
:
454 case JDWP_CMDSET_FIELD
:
457 case JDWP_CMDSET_OBJECTREFERENCE
:
460 case JDWP_CMDSET_STRINGREFERENCE
:
463 case JDWP_CMDSET_THREADREFERENCE
:
466 case JDWP_CMDSET_THREADGROUPREFERENCE
:
469 case JDWP_CMDSET_ARRAYREFERENCE
:
472 case JDWP_CMDSET_CLASSLOADERREFERENCE
:
475 case JDWP_CMDSET_EVENTREQUEST
:
476 CommandsetEventRequestProcess(cmd
);
479 case JDWP_CMDSET_STACKFRAME
:
482 case JDWP_CMDSET_CLASSOBJECTREFERENCE
:
485 case JDWP_CMDSET_EVENT
:
489 // TODO : add exception (or alert)
490 dout("Commandset unknown !!!" << endl
);
495 void JDWServer::CommandsetVirtualMachineProcess(Data
&cmd
)
497 MAKE_JDWPPACKET(rpack
, cmd
);
499 switch (rpack
->u
.command
.command
) {
500 case JDWP_CMD_VERSION
:
504 case JDWP_CMD_ALLCLASSES
:
505 CommandAllClasses(cmd
);
508 case JDWP_CMD_ALLTHREADS
:
509 CommandAllThreads(cmd
);
512 case JDWP_CMD_DISPOSE
:
514 targetrunning
= false;
519 case JDWP_CMD_IDSIZES
:
523 case JDWP_CMD_SUSPEND
:
525 targetrunning
= false;
528 case JDWP_CMD_RESUME
:
530 targetrunning
= true;
533 case JDWP_CMD_CLASSPATHS
:
534 CommandClassPaths(cmd
);
540 void JDWServer::CommandsetEventRequestProcess(Data
&cmd
)
542 MAKE_JDWPPACKET(rpack
, cmd
);
544 switch (rpack
->u
.command
.command
) {
552 void JDWServer::CommandVersion(Data
&cmd
)
554 JDWMessage
msg(acceptfd
);
559 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
561 AddJDWString(response
, offset
, string("RIM JVM"));
562 AddJDWInt(response
, offset
, be_htobl(1));
563 AddJDWInt(response
, offset
, be_htobl(4));
564 AddJDWString(response
, offset
, string("1.4"));
565 AddJDWString(response
, offset
, string("RIM JVM"));
567 response
.ReleaseBuffer(offset
);
570 size_t total_size
= response
.GetSize();
572 // Fill in the header values
573 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
574 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
577 MAKE_JDWPPACKET(rpack
, cmd
);
579 packet
.length
= be_htobl(total_size
);
580 packet
.id
= rpack
->id
;
582 packet
.u
.response
.errorcode
= be_htobs(0);
584 response
.ReleaseBuffer(total_size
);
589 void JDWServer::CommandAllClasses(Data
&cmd
)
594 JDWMessage
msg(acceptfd
);
599 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
601 // Size of known class list
602 size
= visibleClassList
.size() - 1;
604 AddJDWInt(response
, offset
, be_htobl(size
));
606 // Then, write the list of known class
607 for (i
=1; i
<visibleClassList
.size(); i
++) {
608 string str
= visibleClassList
[i
].GetFullClassName();
610 str
= "L" + str
+ ";";
611 replace_if(str
.begin(), str
.end(), bind2nd(equal_to
<char>(),'.'), '/');
613 AddJDWByte(response
, offset
, 0x01);
614 AddJDWInt(response
, offset
, i
); // Should be equal to visibleClassList[i].index
615 AddJDWString(response
, offset
, str
);
616 AddJDWInt(response
, offset
, be_htobl(0x04));
619 response
.ReleaseBuffer(offset
);
622 size_t total_size
= response
.GetSize();
624 // Fill in the header values
625 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
626 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
629 MAKE_JDWPPACKET(rpack
, cmd
);
631 packet
.length
= be_htobl(total_size
);
632 packet
.id
= rpack
->id
;
634 packet
.u
.response
.errorcode
= be_htobs(0);
636 response
.ReleaseBuffer(total_size
);
641 void JDWServer::CommandAllThreads(Data
&cmd
)
643 JDWMessage
msg(acceptfd
);
645 // Get threads list from device
647 jvmdebug
->GetThreadsList(list
);
653 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
655 // Indicate the number of element
656 AddJDWInt(response
, offset
, be_htobl(list
.size()));
658 // Send all threads ID
659 JVMThreadsList::const_iterator b
= list
.begin();
660 for( ; b
!= list
.end(); b
++ ) {
661 const JVMThreadsEntry
&entry
= *b
;
663 AddJDWInt(response
, offset
, be_htobl(entry
.Id
));
666 response
.ReleaseBuffer(offset
);
669 size_t total_size
= response
.GetSize();
671 // Fill in the header values
672 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
673 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
676 MAKE_JDWPPACKET(rpack
, cmd
);
678 packet
.length
= be_htobl(total_size
);
679 packet
.id
= rpack
->id
;
681 packet
.u
.response
.errorcode
= be_htobs(0);
683 response
.ReleaseBuffer(total_size
);
688 void JDWServer::CommandIdSizes(Data
&cmd
)
690 JDWMessage
msg(acceptfd
);
692 MAKE_JDWPPACKET(rpack
, cmd
);
696 Barry::Protocol::JDWP::Packet packet
;
698 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
699 + JDWP_PACKETVIRTUALMACHINEIDSIZES_DATA_SIZE
;
701 packet
.length
= be_htobl(size
);
702 packet
.id
= rpack
->id
;
704 packet
.u
.response
.errorcode
= be_htobs(0);
705 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.fieldIDSize
= be_htobl(0x04);
706 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.methodIDSize
= be_htobl(0x04);
707 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.objectIDSize
= be_htobl(0x04);
708 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.referenceTypeIDSize
= be_htobl(0x04);
709 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.frameIDSize
= be_htobl(0x04);
711 Data
response(&packet
, size
);
717 void JDWServer::CommandSuspend(Data
&cmd
)
719 JDWMessage
msg(acceptfd
);
726 MAKE_JDWPPACKET(rpack
, cmd
);
730 Barry::Protocol::JDWP::Packet packet
;
732 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
734 packet
.length
= be_htobl(size
);
735 packet
.id
= rpack
->id
;
737 packet
.u
.response
.errorcode
= be_htobs(0);
739 Data
response(&packet
, size
);
745 void JDWServer::CommandResume(Data
&cmd
)
747 JDWMessage
msg(acceptfd
);
751 jvmdebug
->Unknown06();
752 jvmdebug
->Unknown07();
753 jvmdebug
->Unknown08();
754 jvmdebug
->Unknown09();
755 jvmdebug
->Unknown10();
759 MAKE_JDWPPACKET(rpack
, cmd
);
763 Barry::Protocol::JDWP::Packet packet
;
765 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
767 packet
.length
= be_htobl(size
);
768 packet
.id
= rpack
->id
;
770 packet
.u
.response
.errorcode
= be_htobs(0);
772 Data
response(&packet
, size
);
778 void JDWServer::CommandClassPaths(Data
&cmd
)
780 JDWMessage
msg(acceptfd
);
785 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
787 AddJDWString(response
, offset
, string(""));
788 AddJDWInt(response
, offset
, be_htobl(0));
789 AddJDWInt(response
, offset
, be_htobl(0));
791 response
.ReleaseBuffer(offset
);
794 size_t total_size
= response
.GetSize();
796 // Fill in the header values
797 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
798 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
801 MAKE_JDWPPACKET(rpack
, cmd
);
803 packet
.length
= be_htobl(total_size
);
804 packet
.id
= rpack
->id
;
806 packet
.u
.response
.errorcode
= be_htobs(0);
808 response
.ReleaseBuffer(total_size
);
814 void JDWServer::CommandSet(Data
&cmd
)
816 static int value
= 2;
818 JDWMessage
msg(acceptfd
);
820 MAKE_JDWPPACKET(rpack
, cmd
);
824 Barry::Protocol::JDWP::Packet packet
;
826 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
+ sizeof(uint32_t);
828 packet
.length
= be_htobl(size
);
829 packet
.id
= rpack
->id
;
831 packet
.u
.response
.errorcode
= be_htobs(0);
832 packet
.u
.response
.u
.value
= be_htobl(value
);
834 Data
response(&packet
, size
);
842 }} // namespace Barry::JDWP