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.
28 #include "j_message.h"
29 #include "protostructs.h"
30 #include "record-internal.h"
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
48 namespace Barry
{ namespace JDWP
{
50 static void * acceptThread(Barry::Thread::CallbackData
*data
);
53 JDWServer::JDWServer(Barry::Mode::JVMDebug
&device
,
54 const char *address
, int port
)
61 , targetrunning(false)
62 , printConsoleMessage(0)
64 SearchDebugFile(debugFileList
);
68 JDWServer::~JDWServer()
74 void JDWServer::SetPasswordDevice(string password
)
76 this->password
= password
;
80 void JDWServer::SetConsoleCallback(ConsoleCallbackType callback
)
82 printConsoleMessage
= callback
;
85 static const char* h_strerror(int code
)
87 // Codes and messages taken from the Linux gethostbyname(3) manpage
91 return _("HOST_NOT_FOUND: The specified host is unknown");
94 return _("NO_ADDRESS: The requested name is valid but does not have an IP address");
97 return _("NO_RECOVERY: A non-recoverable name server error occurred");
100 return _("TRY_AGAIN: A temporary error occurred on an authoritative name server. Try again later.");
103 return _("Unknown network error code");
107 bool JDWServer::Start()
112 struct sockaddr_in sad
;
115 memset((char *) &sad
, '\0', sizeof(struct sockaddr_in
));
118 sad
.sin_addr
.s_addr
= INADDR_ANY
;
120 sad
.sin_addr
.s_addr
= inet_addr(address
.c_str());
122 if (sad
.sin_addr
.s_addr
== INADDR_NONE
) {
123 hp
= gethostbyname(address
.c_str());
126 std::ostringstream oss
;
127 oss
<< "JDWServer::Start: " << h_errno
<< h_strerror(h_errno
);
128 throw Barry::Error(oss
.str());
131 memcpy((char*) &sad
.sin_addr
, (char*) hp
->h_addr
, (size_t) hp
->h_length
);
135 sad
.sin_family
= AF_INET
;
136 sad
.sin_port
= htons((short) (port
& 0xFFFF));
139 sockfd
= socket(sad
.sin_family
, SOCK_STREAM
, 0);
142 throw Barry::ErrnoError(_("JDWServer::Start: Cannot open socket."), errno
);
146 rc
= bind(sockfd
, (struct sockaddr
*) &sad
, sizeof(sad
));
154 throw Barry::ErrnoError(_("JDWServer::Start: Cannot bind socket"), code
);
158 if (listen(sockfd
, SOMAXCONN
) < 0) {
164 throw Barry::ErrnoError(_("JDWServer::Start: Cannot listen on socket"), code
);
167 handler
.reset(new Thread(sockfd
, acceptThread
, (void*) this));
173 static void * acceptThread(Barry::Thread::CallbackData
*data
)
175 JDWServer
*s
= (JDWServer
*) data
->userdata
;
177 while( !data
->stopflag
) {
178 if( s
->AcceptConnection() &&
179 s
->AttachToDevice() &&
180 s
->InitVisibleClassList() &&
183 s
->Run(data
->stopflag
);
184 s
->DetachFromDevice();
192 // Returns true if a new connection was accepted and established
193 bool JDWServer::AcceptConnection()
195 struct sockaddr_in addr
;
196 struct sockaddr
*sa
= (struct sockaddr
*) &addr
;
197 socklen_t addrlen
= sizeof(addr
);
199 acceptfd
= accept(sockfd
, sa
, &addrlen
);
203 fcntl(acceptfd
, F_SETFL
, O_NONBLOCK
);
208 bool JDWServer::AttachToDevice()
210 targetrunning
= false;
212 jvmdebug
->Open(password
.c_str());
215 jvmdebug
->Unknown01();
216 jvmdebug
->Unknown02();
217 jvmdebug
->Unknown03();
218 jvmdebug
->Unknown04();
219 jvmdebug
->Unknown05();
221 jvmdebug
->GetModulesList(modulesList
);
224 // Check debug info for each modules
225 JVMModulesList::const_iterator b
= modulesList
.begin();
226 for ( ; b
!= modulesList
.end(); b
++) {
227 JDG::CodInfo codInfo
;
229 const JVMModulesEntry
&entry
= *b
;
231 bool ret
= LoadDebugInfo(debugFileList
, entry
.UniqueID
, entry
.Name
, codInfo
);
234 appList
[entry
.UniqueID
].Load(codInfo
);
237 dout(_("No debug information found for: ") << entry
.Name
);
238 dout("' (" << hex
<< setfill('0') << setw(8) << entry
.UniqueID
<< ")." << endl
)
246 void JDWServer::DetachFromDevice()
253 #define JDWP_HELLO_STRING "JDWP-Handshake"
257 bool JDWServer::Hello()
261 Barry::Data response
;
263 const size_t len
= strlen(JDWP_HELLO_STRING
);
265 JDWMessage
msg(acceptfd
);
268 ret
= msg
.Receive(response
);
272 size_t size
= response
.GetSize();
273 char *str
= (char *) response
.GetBuffer();
278 if (!strncmp(str
, JDWP_HELLO_STRING
, len
)) {
279 Data
command(JDWP_HELLO_STRING
, len
);
290 void JDWServer::Run(volatile bool &stopflag
)
293 JDWMessage
msg(acceptfd
);
297 MAKE_JDWPPACKET(rpack
, command
);
301 while( loop
&& !stopflag
) {
303 // Read JDWP message from device
304 int value
= jvmdebug
->GetConsoleMessage(str
);
310 ret
= jvmdebug
->GetStatus(status
);
313 // Read JDB message from host
314 msg
.Receive(command
);
316 if (command
.GetSize() > 0) {
318 rpack
= (const Barry::Protocol::JDWP::Packet
*) command
.GetData();
320 if (command
.GetSize() != be_btohl(rpack
->length
)) {
321 dout(_("Packet size error !!!") << endl
);
323 // TODO : add throw exception
328 CommandsetProcess(command
);
333 ret
= jvmdebug
->WaitStatus(status
);
337 if (printConsoleMessage
!= NULL
)
338 printConsoleMessage(str
);
342 // Read JDB message from host
343 msg
.Receive(command
);
345 if (command
.GetSize() > 0) {
347 rpack
= (const Barry::Protocol::JDWP::Packet
*) command
.GetData();
349 if (command
.GetSize() != be_btohl(rpack
->length
)) {
350 dout(_("Packet size error !!!") << endl
);
352 // TODO : add throw exception
357 CommandsetProcess(command
);
366 bool JDWServer::Stop()
368 if( handler
.get() ) {
378 if( acceptfd
>= 0 ) {
387 bool JDWServer::InitVisibleClassList()
392 // it's very ugly, but I want use an index started at '1' inside of '0'
393 // JDB works from '1' :(
395 visibleClassList
.push_back(e
);
397 // Count and index the class (start to '1')
399 JDWAppList::iterator it
;
401 for (it
= appList
.begin(); it
!= appList
.end(); it
++) {
402 JDWAppInfo
&appInfo
= it
->second
;
403 JDG::ClassList
&list
= appInfo
.classList
;
405 JDG::ClassList::iterator b
;
407 for (b
= list
.begin(); b
!= list
.end(); b
++) {
409 // I don't from class field, we have to filter the class view by JDB
410 // if ((b->type != 0x824) && (b->type != 0x64)) {
411 if (b
->id
== 0xffffffff) {
419 visibleClassList
.push_back(*b
);
425 visibleClassList
.CreateDefaultEntries();
431 void JDWServer::CommandsetProcess(Data
&cmd
)
433 MAKE_JDWPPACKET(rpack
, cmd
);
435 switch (rpack
->u
.command
.commandset
) {
436 case JDWP_CMDSET_VIRTUALMACHINE
:
437 CommandsetVirtualMachineProcess(cmd
);
440 case JDWP_CMDSET_REFERECENTYPE
:
443 case JDWP_CMDSET_CLASSTYPE
:
446 case JDWP_CMDSET_ARRAYTYPE
:
449 case JDWP_CMDSET_INTERFACETYPE
:
452 case JDWP_CMDSET_METHOD
:
455 case JDWP_CMDSET_FIELD
:
458 case JDWP_CMDSET_OBJECTREFERENCE
:
461 case JDWP_CMDSET_STRINGREFERENCE
:
464 case JDWP_CMDSET_THREADREFERENCE
:
467 case JDWP_CMDSET_THREADGROUPREFERENCE
:
470 case JDWP_CMDSET_ARRAYREFERENCE
:
473 case JDWP_CMDSET_CLASSLOADERREFERENCE
:
476 case JDWP_CMDSET_EVENTREQUEST
:
477 CommandsetEventRequestProcess(cmd
);
480 case JDWP_CMDSET_STACKFRAME
:
483 case JDWP_CMDSET_CLASSOBJECTREFERENCE
:
486 case JDWP_CMDSET_EVENT
:
490 // TODO : add exception (or alert)
491 dout(_("Commandset unknown !!!") << endl
);
496 void JDWServer::CommandsetVirtualMachineProcess(Data
&cmd
)
498 MAKE_JDWPPACKET(rpack
, cmd
);
500 switch (rpack
->u
.command
.command
) {
501 case JDWP_CMD_VERSION
:
505 case JDWP_CMD_ALLCLASSES
:
506 CommandAllClasses(cmd
);
509 case JDWP_CMD_ALLTHREADS
:
510 CommandAllThreads(cmd
);
513 case JDWP_CMD_DISPOSE
:
515 targetrunning
= false;
520 case JDWP_CMD_IDSIZES
:
524 case JDWP_CMD_SUSPEND
:
526 targetrunning
= false;
529 case JDWP_CMD_RESUME
:
531 targetrunning
= true;
534 case JDWP_CMD_CLASSPATHS
:
535 CommandClassPaths(cmd
);
541 void JDWServer::CommandsetEventRequestProcess(Data
&cmd
)
543 MAKE_JDWPPACKET(rpack
, cmd
);
545 switch (rpack
->u
.command
.command
) {
553 void JDWServer::CommandVersion(Data
&cmd
)
555 JDWMessage
msg(acceptfd
);
560 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
562 AddJDWString(response
, offset
, string("RIM JVM"));
563 AddJDWInt(response
, offset
, be_htobl(1));
564 AddJDWInt(response
, offset
, be_htobl(4));
565 AddJDWString(response
, offset
, string("1.4"));
566 AddJDWString(response
, offset
, string("RIM JVM"));
568 response
.ReleaseBuffer(offset
);
571 size_t total_size
= response
.GetSize();
573 // Fill in the header values
574 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
575 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
578 MAKE_JDWPPACKET(rpack
, cmd
);
580 packet
.length
= be_htobl(total_size
);
581 packet
.id
= rpack
->id
;
583 packet
.u
.response
.errorcode
= be_htobs(0);
585 response
.ReleaseBuffer(total_size
);
590 void JDWServer::CommandAllClasses(Data
&cmd
)
595 JDWMessage
msg(acceptfd
);
600 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
602 // Size of known class list
603 size
= visibleClassList
.size() - 1;
605 AddJDWInt(response
, offset
, be_htobl(size
));
607 // Then, write the list of known class
608 for (i
=1; i
<visibleClassList
.size(); i
++) {
609 string str
= visibleClassList
[i
].GetFullClassName();
611 str
= "L" + str
+ ";";
612 replace_if(str
.begin(), str
.end(), bind2nd(equal_to
<char>(),'.'), '/');
614 AddJDWByte(response
, offset
, 0x01);
615 AddJDWInt(response
, offset
, i
); // Should be equal to visibleClassList[i].index
616 AddJDWString(response
, offset
, str
);
617 AddJDWInt(response
, offset
, be_htobl(0x04));
620 response
.ReleaseBuffer(offset
);
623 size_t total_size
= response
.GetSize();
625 // Fill in the header values
626 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
627 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
630 MAKE_JDWPPACKET(rpack
, cmd
);
632 packet
.length
= be_htobl(total_size
);
633 packet
.id
= rpack
->id
;
635 packet
.u
.response
.errorcode
= be_htobs(0);
637 response
.ReleaseBuffer(total_size
);
642 void JDWServer::CommandAllThreads(Data
&cmd
)
644 JDWMessage
msg(acceptfd
);
646 // Get threads list from device
648 jvmdebug
->GetThreadsList(list
);
654 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
656 // Indicate the number of element
657 AddJDWInt(response
, offset
, be_htobl(list
.size()));
659 // Send all threads ID
660 JVMThreadsList::const_iterator b
= list
.begin();
661 for( ; b
!= list
.end(); b
++ ) {
662 const JVMThreadsEntry
&entry
= *b
;
664 AddJDWInt(response
, offset
, be_htobl(entry
.Id
));
667 response
.ReleaseBuffer(offset
);
670 size_t total_size
= response
.GetSize();
672 // Fill in the header values
673 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
674 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
677 MAKE_JDWPPACKET(rpack
, cmd
);
679 packet
.length
= be_htobl(total_size
);
680 packet
.id
= rpack
->id
;
682 packet
.u
.response
.errorcode
= be_htobs(0);
684 response
.ReleaseBuffer(total_size
);
689 void JDWServer::CommandIdSizes(Data
&cmd
)
691 JDWMessage
msg(acceptfd
);
693 MAKE_JDWPPACKET(rpack
, cmd
);
697 Barry::Protocol::JDWP::Packet packet
;
699 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
700 + JDWP_PACKETVIRTUALMACHINEIDSIZES_DATA_SIZE
;
702 packet
.length
= be_htobl(size
);
703 packet
.id
= rpack
->id
;
705 packet
.u
.response
.errorcode
= be_htobs(0);
706 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.fieldIDSize
= be_htobl(0x04);
707 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.methodIDSize
= be_htobl(0x04);
708 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.objectIDSize
= be_htobl(0x04);
709 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.referenceTypeIDSize
= be_htobl(0x04);
710 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.frameIDSize
= be_htobl(0x04);
712 Data
response(&packet
, size
);
718 void JDWServer::CommandSuspend(Data
&cmd
)
720 JDWMessage
msg(acceptfd
);
727 MAKE_JDWPPACKET(rpack
, cmd
);
731 Barry::Protocol::JDWP::Packet packet
;
733 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
735 packet
.length
= be_htobl(size
);
736 packet
.id
= rpack
->id
;
738 packet
.u
.response
.errorcode
= be_htobs(0);
740 Data
response(&packet
, size
);
746 void JDWServer::CommandResume(Data
&cmd
)
748 JDWMessage
msg(acceptfd
);
752 jvmdebug
->Unknown06();
753 jvmdebug
->Unknown07();
754 jvmdebug
->Unknown08();
755 jvmdebug
->Unknown09();
756 jvmdebug
->Unknown10();
760 MAKE_JDWPPACKET(rpack
, cmd
);
764 Barry::Protocol::JDWP::Packet packet
;
766 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
768 packet
.length
= be_htobl(size
);
769 packet
.id
= rpack
->id
;
771 packet
.u
.response
.errorcode
= be_htobs(0);
773 Data
response(&packet
, size
);
779 void JDWServer::CommandClassPaths(Data
&cmd
)
781 JDWMessage
msg(acceptfd
);
786 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
788 AddJDWString(response
, offset
, string(""));
789 AddJDWInt(response
, offset
, be_htobl(0));
790 AddJDWInt(response
, offset
, be_htobl(0));
792 response
.ReleaseBuffer(offset
);
795 size_t total_size
= response
.GetSize();
797 // Fill in the header values
798 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
799 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
802 MAKE_JDWPPACKET(rpack
, cmd
);
804 packet
.length
= be_htobl(total_size
);
805 packet
.id
= rpack
->id
;
807 packet
.u
.response
.errorcode
= be_htobs(0);
809 response
.ReleaseBuffer(total_size
);
815 void JDWServer::CommandSet(Data
&cmd
)
817 static int value
= 2;
819 JDWMessage
msg(acceptfd
);
821 MAKE_JDWPPACKET(rpack
, cmd
);
825 Barry::Protocol::JDWP::Packet packet
;
827 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
+ sizeof(uint32_t);
829 packet
.length
= be_htobl(size
);
830 packet
.id
= rpack
->id
;
832 packet
.u
.response
.errorcode
= be_htobs(0);
833 packet
.u
.response
.u
.value
= be_htobl(value
);
835 Data
response(&packet
, size
);
843 }} // namespace Barry::JDWP