blob: 9f230098b40c14f0997dd038d40fed41aba62b4e [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10
11#include "GDBRemoteCommunication.h"
12
13// C Includes
Johnny Chena5663552011-05-13 20:07:25 +000014#include <limits.h>
Stephen Wilsona78867b2011-03-25 18:16:28 +000015#include <string.h>
Greg Clayton91a9b2472013-12-04 19:19:12 +000016#include <sys/stat.h>
Stephen Wilsona78867b2011-03-25 18:16:28 +000017
Chris Lattner30fdc8d2010-06-08 16:52:24 +000018// C++ Includes
19// Other libraries and framework includes
Greg Clayton00fe87b2013-12-05 22:58:22 +000020#include "lldb/Core/ConnectionFileDescriptor.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000021#include "lldb/Core/Log.h"
Greg Claytonc1422c12012-04-09 22:46:21 +000022#include "lldb/Core/StreamFile.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000023#include "lldb/Core/StreamString.h"
Greg Clayton8b82f082011-04-12 05:54:46 +000024#include "lldb/Host/FileSpec.h"
Zachary Turnerc00cf4a2014-08-15 22:04:21 +000025#include "lldb/Host/FileSystem.h"
Greg Clayton8b82f082011-04-12 05:54:46 +000026#include "lldb/Host/Host.h"
Zachary Turner42ff0ad2014-08-21 17:29:12 +000027#include "lldb/Host/HostInfo.h"
Zachary Turner98688922014-08-06 18:16:26 +000028#include "lldb/Host/Socket.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000029#include "lldb/Host/TimeValue.h"
Greg Clayton8b82f082011-04-12 05:54:46 +000030#include "lldb/Target/Process.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000031
32// Project includes
Chris Lattner30fdc8d2010-06-08 16:52:24 +000033#include "ProcessGDBRemoteLog.h"
34
Todd Fiala015d8182014-07-22 23:41:36 +000035#if defined(__APPLE__)
36# define DEBUGSERVER_BASENAME "debugserver"
37#else
38# define DEBUGSERVER_BASENAME "lldb-gdbserver"
39#endif
Greg Clayton8b82f082011-04-12 05:54:46 +000040
Chris Lattner30fdc8d2010-06-08 16:52:24 +000041using namespace lldb;
42using namespace lldb_private;
43
Greg Claytonc1422c12012-04-09 22:46:21 +000044GDBRemoteCommunication::History::History (uint32_t size) :
45 m_packets(),
46 m_curr_idx (0),
47 m_total_packet_count (0),
48 m_dumped_to_log (false)
49{
50 m_packets.resize(size);
51}
52
53GDBRemoteCommunication::History::~History ()
54{
55}
56
57void
Greg Claytond451c1a2012-04-13 21:24:18 +000058GDBRemoteCommunication::History::AddPacket (char packet_char,
59 PacketType type,
60 uint32_t bytes_transmitted)
61{
62 const size_t size = m_packets.size();
63 if (size > 0)
64 {
65 const uint32_t idx = GetNextIndex();
66 m_packets[idx].packet.assign (1, packet_char);
67 m_packets[idx].type = type;
68 m_packets[idx].bytes_transmitted = bytes_transmitted;
69 m_packets[idx].packet_idx = m_total_packet_count;
70 m_packets[idx].tid = Host::GetCurrentThreadID();
71 }
72}
73
74void
75GDBRemoteCommunication::History::AddPacket (const std::string &src,
76 uint32_t src_len,
77 PacketType type,
78 uint32_t bytes_transmitted)
79{
80 const size_t size = m_packets.size();
81 if (size > 0)
82 {
83 const uint32_t idx = GetNextIndex();
84 m_packets[idx].packet.assign (src, 0, src_len);
85 m_packets[idx].type = type;
86 m_packets[idx].bytes_transmitted = bytes_transmitted;
87 m_packets[idx].packet_idx = m_total_packet_count;
88 m_packets[idx].tid = Host::GetCurrentThreadID();
89 }
90}
91
92void
Greg Claytonc1422c12012-04-09 22:46:21 +000093GDBRemoteCommunication::History::Dump (lldb_private::Stream &strm) const
94{
95 const uint32_t size = GetNumPacketsInHistory ();
96 const uint32_t first_idx = GetFirstSavedPacketIndex ();
97 const uint32_t stop_idx = m_curr_idx + size;
98 for (uint32_t i = first_idx; i < stop_idx; ++i)
99 {
100 const uint32_t idx = NormalizeIndex (i);
101 const Entry &entry = m_packets[idx];
102 if (entry.type == ePacketTypeInvalid || entry.packet.empty())
103 break;
Daniel Malead01b2952012-11-29 21:49:15 +0000104 strm.Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n",
Greg Claytonc1422c12012-04-09 22:46:21 +0000105 entry.packet_idx,
Greg Claytond451c1a2012-04-13 21:24:18 +0000106 entry.tid,
Greg Claytonc1422c12012-04-09 22:46:21 +0000107 entry.bytes_transmitted,
108 (entry.type == ePacketTypeSend) ? "send" : "read",
109 entry.packet.c_str());
110 }
111}
112
113void
114GDBRemoteCommunication::History::Dump (lldb_private::Log *log) const
115{
116 if (log && !m_dumped_to_log)
117 {
118 m_dumped_to_log = true;
119 const uint32_t size = GetNumPacketsInHistory ();
120 const uint32_t first_idx = GetFirstSavedPacketIndex ();
121 const uint32_t stop_idx = m_curr_idx + size;
122 for (uint32_t i = first_idx; i < stop_idx; ++i)
123 {
124 const uint32_t idx = NormalizeIndex (i);
125 const Entry &entry = m_packets[idx];
126 if (entry.type == ePacketTypeInvalid || entry.packet.empty())
127 break;
Daniel Malead01b2952012-11-29 21:49:15 +0000128 log->Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s",
Greg Claytonc1422c12012-04-09 22:46:21 +0000129 entry.packet_idx,
Greg Claytond451c1a2012-04-13 21:24:18 +0000130 entry.tid,
Greg Claytonc1422c12012-04-09 22:46:21 +0000131 entry.bytes_transmitted,
132 (entry.type == ePacketTypeSend) ? "send" : "read",
133 entry.packet.c_str());
134 }
135 }
136}
137
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000138//----------------------------------------------------------------------
139// GDBRemoteCommunication constructor
140//----------------------------------------------------------------------
Greg Clayton8b82f082011-04-12 05:54:46 +0000141GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name,
142 const char *listener_name,
143 bool is_platform) :
Greg Clayton576d8832011-03-22 04:00:09 +0000144 Communication(comm_name),
Daniel Maleae0f8f572013-08-26 23:57:52 +0000145#ifdef LLDB_CONFIGURATION_DEBUG
146 m_packet_timeout (1000),
147#else
Greg Claytonf3dd93c2011-06-17 03:31:01 +0000148 m_packet_timeout (1),
Daniel Maleae0f8f572013-08-26 23:57:52 +0000149#endif
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000150 m_sequence_mutex (Mutex::eMutexTypeRecursive),
Greg Clayton4dc72282011-01-20 07:53:45 +0000151 m_public_is_running (false),
Greg Clayton1cb64962011-03-24 04:28:38 +0000152 m_private_is_running (false),
Greg Claytonc1422c12012-04-09 22:46:21 +0000153 m_history (512),
Greg Clayton8b82f082011-04-12 05:54:46 +0000154 m_send_acks (true),
Greg Clayton00fe87b2013-12-05 22:58:22 +0000155 m_is_platform (is_platform),
156 m_listen_thread (LLDB_INVALID_HOST_THREAD),
157 m_listen_url ()
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000158{
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000159}
160
161//----------------------------------------------------------------------
162// Destructor
163//----------------------------------------------------------------------
164GDBRemoteCommunication::~GDBRemoteCommunication()
165{
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000166 if (IsConnected())
167 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000168 Disconnect();
169 }
170}
171
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000172char
173GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length)
174{
175 int checksum = 0;
176
Ed Mastea6b4c772013-08-20 14:12:58 +0000177 for (size_t i = 0; i < payload_length; ++i)
178 checksum += payload[i];
179
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000180 return checksum & 255;
181}
182
183size_t
Greg Clayton6ed95942011-01-22 07:12:45 +0000184GDBRemoteCommunication::SendAck ()
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000185{
Greg Clayton5160ce52013-03-27 23:08:40 +0000186 Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000187 ConnectionStatus status = eConnectionStatusSuccess;
Greg Claytonc1422c12012-04-09 22:46:21 +0000188 char ch = '+';
189 const size_t bytes_written = Write (&ch, 1, status, NULL);
190 if (log)
Greg Clayton45989072013-10-23 18:24:30 +0000191 log->Printf ("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
Greg Claytonc1422c12012-04-09 22:46:21 +0000192 m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written);
193 return bytes_written;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000194}
195
196size_t
Greg Clayton6ed95942011-01-22 07:12:45 +0000197GDBRemoteCommunication::SendNack ()
198{
Greg Clayton5160ce52013-03-27 23:08:40 +0000199 Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
Greg Clayton6ed95942011-01-22 07:12:45 +0000200 ConnectionStatus status = eConnectionStatusSuccess;
Greg Claytonc1422c12012-04-09 22:46:21 +0000201 char ch = '-';
202 const size_t bytes_written = Write (&ch, 1, status, NULL);
203 if (log)
Greg Clayton45989072013-10-23 18:24:30 +0000204 log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
Greg Claytonc1422c12012-04-09 22:46:21 +0000205 m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written);
206 return bytes_written;
Greg Clayton32e0a752011-03-30 18:16:51 +0000207}
208
Greg Clayton3dedae12013-12-06 21:45:27 +0000209GDBRemoteCommunication::PacketResult
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000210GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length)
211{
212 Mutex::Locker locker(m_sequence_mutex);
213 return SendPacketNoLock (payload, payload_length);
214}
215
Greg Clayton3dedae12013-12-06 21:45:27 +0000216GDBRemoteCommunication::PacketResult
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000217GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length)
218{
219 if (IsConnected())
220 {
221 StreamString packet(0, 4, eByteOrderBig);
222
223 packet.PutChar('$');
224 packet.Write (payload, payload_length);
225 packet.PutChar('#');
226 packet.PutHex8(CalculcateChecksum (payload, payload_length));
227
Greg Clayton5160ce52013-03-27 23:08:40 +0000228 Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000229 ConnectionStatus status = eConnectionStatusSuccess;
230 size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL);
Greg Claytonc1422c12012-04-09 22:46:21 +0000231 if (log)
232 {
233 // If logging was just enabled and we have history, then dump out what
234 // we have to the log so we get the historical context. The Dump() call that
235 // logs all of the packet will set a boolean so that we don't dump this more
236 // than once
237 if (!m_history.DidDumpToLog ())
Greg Clayton5160ce52013-03-27 23:08:40 +0000238 m_history.Dump (log);
Greg Claytonc1422c12012-04-09 22:46:21 +0000239
Greg Clayton45989072013-10-23 18:24:30 +0000240 log->Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, (int)packet.GetSize(), packet.GetData());
Greg Claytonc1422c12012-04-09 22:46:21 +0000241 }
242
243 m_history.AddPacket (packet.GetString(), packet.GetSize(), History::ePacketTypeSend, bytes_written);
244
245
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000246 if (bytes_written == packet.GetSize())
247 {
Greg Clayton71fc2a32011-02-12 06:28:37 +0000248 if (GetSendAcks ())
Greg Clayton3dedae12013-12-06 21:45:27 +0000249 return GetAck ();
250 else
251 return PacketResult::Success;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000252 }
Johnny Chend0c40dd2010-09-14 22:10:43 +0000253 else
254 {
Greg Clayton6d093452011-02-05 02:25:06 +0000255 if (log)
Greg Clayton5fe15d22011-05-20 03:15:54 +0000256 log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData());
Johnny Chend0c40dd2010-09-14 22:10:43 +0000257 }
Greg Claytonf5e56de2010-09-14 23:36:40 +0000258 }
Greg Clayton3dedae12013-12-06 21:45:27 +0000259 return PacketResult::ErrorSendFailed;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000260}
261
Greg Clayton3dedae12013-12-06 21:45:27 +0000262GDBRemoteCommunication::PacketResult
Greg Claytonc574ede2011-03-10 02:26:48 +0000263GDBRemoteCommunication::GetAck ()
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000264{
Greg Clayton576d8832011-03-22 04:00:09 +0000265 StringExtractorGDBRemote packet;
Greg Clayton3dedae12013-12-06 21:45:27 +0000266 PacketResult result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds ());
267 if (result == PacketResult::Success)
268 {
269 if (packet.GetResponseType() == StringExtractorGDBRemote::ResponseType::eAck)
270 return PacketResult::Success;
271 else
272 return PacketResult::ErrorSendAck;
273 }
274 return result;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000275}
276
277bool
Jim Ingham4ceb9282012-06-08 22:50:40 +0000278GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker, const char *failure_message)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000279{
Greg Claytond9896732012-05-31 16:54:51 +0000280 if (IsRunning())
Jim Ingham4ceb9282012-06-08 22:50:40 +0000281 return locker.TryLock (m_sequence_mutex, failure_message);
Greg Claytond9896732012-05-31 16:54:51 +0000282
283 locker.Lock (m_sequence_mutex);
284 return true;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000285}
286
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000287
Greg Clayton6779606a2011-01-22 23:43:18 +0000288bool
Greg Clayton6779606a2011-01-22 23:43:18 +0000289GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr)
290{
291 return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL);
292}
293
Greg Clayton3dedae12013-12-06 21:45:27 +0000294GDBRemoteCommunication::PacketResult
Greg Clayton73bf5db2011-06-17 01:22:15 +0000295GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000296{
Greg Clayton73bf5db2011-06-17 01:22:15 +0000297 uint8_t buffer[8192];
298 Error error;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000299
Greg Clayton5160ce52013-03-27 23:08:40 +0000300 Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE));
Greg Clayton644247c2011-07-07 01:59:51 +0000301
Greg Clayton73bf5db2011-06-17 01:22:15 +0000302 // Check for a packet from our cache first without trying any reading...
303 if (CheckForPacket (NULL, 0, packet))
Greg Clayton3dedae12013-12-06 21:45:27 +0000304 return PacketResult::Success;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000305
Greg Clayton0c51ac32011-07-02 23:21:06 +0000306 bool timed_out = false;
Greg Clayton3dedae12013-12-06 21:45:27 +0000307 bool disconnected = false;
Greg Clayton0c51ac32011-07-02 23:21:06 +0000308 while (IsConnected() && !timed_out)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000309 {
Johnny Chen74549c82011-07-19 01:13:00 +0000310 lldb::ConnectionStatus status = eConnectionStatusNoConnection;
Greg Clayton73bf5db2011-06-17 01:22:15 +0000311 size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error);
Greg Clayton644247c2011-07-07 01:59:51 +0000312
313 if (log)
Daniel Malead01b2952012-11-29 21:49:15 +0000314 log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %" PRIu64,
Greg Clayton644247c2011-07-07 01:59:51 +0000315 __PRETTY_FUNCTION__,
316 timeout_usec,
317 Communication::ConnectionStatusAsCString (status),
318 error.AsCString(),
Greg Clayton43e0af02012-09-18 18:04:04 +0000319 (uint64_t)bytes_read);
Greg Clayton644247c2011-07-07 01:59:51 +0000320
Greg Clayton73bf5db2011-06-17 01:22:15 +0000321 if (bytes_read > 0)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000322 {
Greg Clayton73bf5db2011-06-17 01:22:15 +0000323 if (CheckForPacket (buffer, bytes_read, packet))
Greg Clayton3dedae12013-12-06 21:45:27 +0000324 return PacketResult::Success;
Greg Clayton73bf5db2011-06-17 01:22:15 +0000325 }
326 else
327 {
328 switch (status)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000329 {
Greg Clayton197bacf2011-07-02 21:07:54 +0000330 case eConnectionStatusTimedOut:
Greg Claytonf0066ad2014-05-02 00:45:31 +0000331 case eConnectionStatusInterrupted:
Greg Clayton0c51ac32011-07-02 23:21:06 +0000332 timed_out = true;
333 break;
334 case eConnectionStatusSuccess:
335 //printf ("status = success but error = %s\n", error.AsCString("<invalid>"));
Greg Clayton73bf5db2011-06-17 01:22:15 +0000336 break;
337
338 case eConnectionStatusEndOfFile:
339 case eConnectionStatusNoConnection:
340 case eConnectionStatusLostConnection:
341 case eConnectionStatusError:
Greg Clayton3dedae12013-12-06 21:45:27 +0000342 disconnected = true;
Greg Clayton73bf5db2011-06-17 01:22:15 +0000343 Disconnect();
344 break;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000345 }
346 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000347 }
Greg Clayton3dedae12013-12-06 21:45:27 +0000348 packet.Clear ();
349 if (disconnected)
350 return PacketResult::ErrorDisconnected;
351 if (timed_out)
352 return PacketResult::ErrorReplyTimeout;
353 else
354 return PacketResult::ErrorReplyFailed;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000355}
356
Greg Clayton73bf5db2011-06-17 01:22:15 +0000357bool
358GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000359{
360 // Put the packet data into the buffer in a thread safe fashion
361 Mutex::Locker locker(m_bytes_mutex);
Greg Clayton197bacf2011-07-02 21:07:54 +0000362
Greg Clayton5160ce52013-03-27 23:08:40 +0000363 Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
Greg Clayton197bacf2011-07-02 21:07:54 +0000364
Greg Clayton73bf5db2011-06-17 01:22:15 +0000365 if (src && src_len > 0)
Greg Clayton197bacf2011-07-02 21:07:54 +0000366 {
Greg Clayton0c51ac32011-07-02 23:21:06 +0000367 if (log && log->GetVerbose())
Greg Clayton197bacf2011-07-02 21:07:54 +0000368 {
369 StreamString s;
Greg Clayton0c51ac32011-07-02 23:21:06 +0000370 log->Printf ("GDBRemoteCommunication::%s adding %u bytes: %.*s",
371 __FUNCTION__,
372 (uint32_t)src_len,
373 (uint32_t)src_len,
374 src);
Greg Clayton197bacf2011-07-02 21:07:54 +0000375 }
Greg Clayton73bf5db2011-06-17 01:22:15 +0000376 m_bytes.append ((const char *)src, src_len);
Greg Clayton197bacf2011-07-02 21:07:54 +0000377 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000378
379 // Parse up the packets into gdb remote packets
Greg Clayton197bacf2011-07-02 21:07:54 +0000380 if (!m_bytes.empty())
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000381 {
382 // end_idx must be one past the last valid packet byte. Start
383 // it off with an invalid value that is the same as the current
384 // index.
Greg Clayton73bf5db2011-06-17 01:22:15 +0000385 size_t content_start = 0;
386 size_t content_length = 0;
387 size_t total_length = 0;
388 size_t checksum_idx = std::string::npos;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000389
390 switch (m_bytes[0])
391 {
392 case '+': // Look for ack
393 case '-': // Look for cancel
394 case '\x03': // ^C to halt target
Greg Clayton73bf5db2011-06-17 01:22:15 +0000395 content_length = total_length = 1; // The command is one byte long...
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000396 break;
397
398 case '$':
399 // Look for a standard gdb packet?
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000400 {
Greg Clayton73bf5db2011-06-17 01:22:15 +0000401 size_t hash_pos = m_bytes.find('#');
402 if (hash_pos != std::string::npos)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000403 {
Greg Clayton73bf5db2011-06-17 01:22:15 +0000404 if (hash_pos + 2 < m_bytes.size())
405 {
406 checksum_idx = hash_pos + 1;
407 // Skip the dollar sign
408 content_start = 1;
409 // Don't include the # in the content or the $ in the content length
410 content_length = hash_pos - 1;
411
412 total_length = hash_pos + 3; // Skip the # and the two hex checksum bytes
413 }
414 else
415 {
416 // Checksum bytes aren't all here yet
417 content_length = std::string::npos;
418 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000419 }
420 }
421 break;
422
423 default:
Greg Claytonf3dd93c2011-06-17 03:31:01 +0000424 {
Greg Clayton197bacf2011-07-02 21:07:54 +0000425 // We have an unexpected byte and we need to flush all bad
426 // data that is in m_bytes, so we need to find the first
427 // byte that is a '+' (ACK), '-' (NACK), \x03 (CTRL+C interrupt),
428 // or '$' character (start of packet header) or of course,
429 // the end of the data in m_bytes...
430 const size_t bytes_len = m_bytes.size();
431 bool done = false;
432 uint32_t idx;
433 for (idx = 1; !done && idx < bytes_len; ++idx)
434 {
435 switch (m_bytes[idx])
436 {
437 case '+':
438 case '-':
439 case '\x03':
440 case '$':
441 done = true;
442 break;
443
444 default:
445 break;
446 }
447 }
Greg Claytonf3dd93c2011-06-17 03:31:01 +0000448 if (log)
Greg Clayton197bacf2011-07-02 21:07:54 +0000449 log->Printf ("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'",
450 __FUNCTION__, idx, idx, m_bytes.c_str());
451 m_bytes.erase(0, idx);
Greg Claytonf3dd93c2011-06-17 03:31:01 +0000452 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000453 break;
454 }
455
Greg Clayton73bf5db2011-06-17 01:22:15 +0000456 if (content_length == std::string::npos)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000457 {
Greg Clayton73bf5db2011-06-17 01:22:15 +0000458 packet.Clear();
459 return false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000460 }
Greg Claytonf3dd93c2011-06-17 03:31:01 +0000461 else if (total_length > 0)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000462 {
Greg Clayton73bf5db2011-06-17 01:22:15 +0000463
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000464 // We have a valid packet...
Greg Clayton73bf5db2011-06-17 01:22:15 +0000465 assert (content_length <= m_bytes.size());
466 assert (total_length <= m_bytes.size());
467 assert (content_length <= total_length);
Greg Clayton06f09b52014-06-20 20:41:07 +0000468 const size_t content_end = content_start + content_length;
469
Greg Clayton73bf5db2011-06-17 01:22:15 +0000470 bool success = true;
471 std::string &packet_str = packet.GetStringRef();
Greg Claytonc1422c12012-04-09 22:46:21 +0000472
473
474 if (log)
475 {
476 // If logging was just enabled and we have history, then dump out what
477 // we have to the log so we get the historical context. The Dump() call that
478 // logs all of the packet will set a boolean so that we don't dump this more
479 // than once
480 if (!m_history.DidDumpToLog ())
Greg Clayton5160ce52013-03-27 23:08:40 +0000481 m_history.Dump (log);
Greg Claytonc1422c12012-04-09 22:46:21 +0000482
Greg Clayton06f09b52014-06-20 20:41:07 +0000483 bool binary = false;
484 // Only detect binary for packets that start with a '$' and have a '#CC' checksum
485 if (m_bytes[0] == '$' && total_length > 4)
486 {
487 for (size_t i=0; !binary && i<total_length; ++i)
488 {
489 if (isprint(m_bytes[i]) == 0)
490 binary = true;
491 }
492 }
493 if (binary)
494 {
495 StreamString strm;
496 // Packet header...
497 strm.Printf("<%4" PRIu64 "> read packet: %c", (uint64_t)total_length, m_bytes[0]);
498 for (size_t i=content_start; i<content_end; ++i)
499 {
500 // Remove binary escaped bytes when displaying the packet...
501 const char ch = m_bytes[i];
502 if (ch == 0x7d)
503 {
504 // 0x7d is the escape character. The next character is to
505 // be XOR'd with 0x20.
506 const char escapee = m_bytes[++i] ^ 0x20;
507 strm.Printf("%2.2x", escapee);
508 }
509 else
510 {
511 strm.Printf("%2.2x", (uint8_t)ch);
512 }
513 }
514 // Packet footer...
515 strm.Printf("%c%c%c", m_bytes[total_length-3], m_bytes[total_length-2], m_bytes[total_length-1]);
516 log->PutCString(strm.GetString().c_str());
517 }
518 else
519 {
520 log->Printf("<%4" PRIu64 "> read packet: %.*s", (uint64_t)total_length, (int)(total_length), m_bytes.c_str());
521 }
Greg Claytonc1422c12012-04-09 22:46:21 +0000522 }
523
524 m_history.AddPacket (m_bytes.c_str(), total_length, History::ePacketTypeRecv, total_length);
525
Hafiz Abid Qadeere5fd5e12013-08-28 15:10:37 +0000526 // Clear packet_str in case there is some existing data in it.
527 packet_str.clear();
Hafiz Abid Qadeerda96ef22013-08-28 10:31:52 +0000528 // Copy the packet from m_bytes to packet_str expanding the
529 // run-length encoding in the process.
530 // Reserve enough byte for the most common case (no RLE used)
531 packet_str.reserve(m_bytes.length());
Greg Clayton06f09b52014-06-20 20:41:07 +0000532 for (std::string::const_iterator c = m_bytes.begin() + content_start; c != m_bytes.begin() + content_end; ++c)
Hafiz Abid Qadeerda96ef22013-08-28 10:31:52 +0000533 {
534 if (*c == '*')
535 {
536 // '*' indicates RLE. Next character will give us the
537 // repeat count and previous character is what is to be
538 // repeated.
539 char char_to_repeat = packet_str.back();
540 // Number of time the previous character is repeated
541 int repeat_count = *++c + 3 - ' ';
542 // We have the char_to_repeat and repeat_count. Now push
543 // it in the packet.
544 for (int i = 0; i < repeat_count; ++i)
545 packet_str.push_back(char_to_repeat);
546 }
Steve Pucci3c5d3332014-02-24 19:07:29 +0000547 else if (*c == 0x7d)
548 {
549 // 0x7d is the escape character. The next character is to
550 // be XOR'd with 0x20.
551 char escapee = *++c ^ 0x20;
552 packet_str.push_back(escapee);
553 }
Hafiz Abid Qadeerda96ef22013-08-28 10:31:52 +0000554 else
555 {
556 packet_str.push_back(*c);
557 }
558 }
559
Greg Clayton73bf5db2011-06-17 01:22:15 +0000560 if (m_bytes[0] == '$')
561 {
562 assert (checksum_idx < m_bytes.size());
563 if (::isxdigit (m_bytes[checksum_idx+0]) ||
564 ::isxdigit (m_bytes[checksum_idx+1]))
565 {
566 if (GetSendAcks ())
567 {
568 const char *packet_checksum_cstr = &m_bytes[checksum_idx];
569 char packet_checksum = strtol (packet_checksum_cstr, NULL, 16);
570 char actual_checksum = CalculcateChecksum (packet_str.c_str(), packet_str.size());
571 success = packet_checksum == actual_checksum;
572 if (!success)
573 {
574 if (log)
575 log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x",
576 (int)(total_length),
577 m_bytes.c_str(),
578 (uint8_t)packet_checksum,
579 (uint8_t)actual_checksum);
580 }
581 // Send the ack or nack if needed
582 if (!success)
583 SendNack();
584 else
585 SendAck();
586 }
Greg Clayton73bf5db2011-06-17 01:22:15 +0000587 }
588 else
589 {
590 success = false;
591 if (log)
Jason Molendafd54b362011-09-20 21:44:10 +0000592 log->Printf ("error: invalid checksum in packet: '%s'\n", m_bytes.c_str());
Greg Clayton73bf5db2011-06-17 01:22:15 +0000593 }
594 }
Greg Claytonc1422c12012-04-09 22:46:21 +0000595
Greg Clayton73bf5db2011-06-17 01:22:15 +0000596 m_bytes.erase(0, total_length);
597 packet.SetFilePos(0);
598 return success;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000599 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000600 }
Greg Clayton73bf5db2011-06-17 01:22:15 +0000601 packet.Clear();
602 return false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000603}
604
Greg Clayton8b82f082011-04-12 05:54:46 +0000605Error
Greg Claytond6299802013-12-06 17:46:35 +0000606GDBRemoteCommunication::StartListenThread (const char *hostname, uint16_t port)
Greg Clayton00fe87b2013-12-05 22:58:22 +0000607{
608 Error error;
609 if (IS_VALID_LLDB_HOST_THREAD(m_listen_thread))
610 {
611 error.SetErrorString("listen thread already running");
612 }
613 else
614 {
615 char listen_url[512];
616 if (hostname && hostname[0])
Jean-Daniel Dupas3c6774a2014-02-08 20:29:40 +0000617 snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname, port);
Greg Clayton00fe87b2013-12-05 22:58:22 +0000618 else
619 snprintf(listen_url, sizeof(listen_url), "listen://%i", port);
620 m_listen_url = listen_url;
621 SetConnection(new ConnectionFileDescriptor());
622 m_listen_thread = Host::ThreadCreate (listen_url, GDBRemoteCommunication::ListenThread, this, &error);
623 }
624 return error;
625}
626
627bool
628GDBRemoteCommunication::JoinListenThread ()
629{
630 if (IS_VALID_LLDB_HOST_THREAD(m_listen_thread))
631 {
632 Host::ThreadJoin(m_listen_thread, NULL, NULL);
633 m_listen_thread = LLDB_INVALID_HOST_THREAD;
634 }
635 return true;
636}
637
638lldb::thread_result_t
639GDBRemoteCommunication::ListenThread (lldb::thread_arg_t arg)
640{
641 GDBRemoteCommunication *comm = (GDBRemoteCommunication *)arg;
642 Error error;
643 ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)comm->GetConnection ();
644
645 if (connection)
646 {
647 // Do the listen on another thread so we can continue on...
648 if (connection->Connect(comm->m_listen_url.c_str(), &error) != eConnectionStatusSuccess)
649 comm->SetConnection(NULL);
650 }
651 return NULL;
652}
653
654Error
Greg Claytonfda4fab2014-01-10 22:24:11 +0000655GDBRemoteCommunication::StartDebugserverProcess (const char *hostname,
656 uint16_t in_port,
Greg Clayton91a9b2472013-12-04 19:19:12 +0000657 lldb_private::ProcessLaunchInfo &launch_info,
Greg Claytonfda4fab2014-01-10 22:24:11 +0000658 uint16_t &out_port)
Greg Clayton8b82f082011-04-12 05:54:46 +0000659{
Todd Fiala015d8182014-07-22 23:41:36 +0000660 Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
661 if (log)
662 log->Printf ("GDBRemoteCommunication::%s(hostname=%s, in_port=%" PRIu16 ", out_port=%" PRIu16, __FUNCTION__, hostname ? hostname : "<empty>", in_port, out_port);
663
Greg Claytonfda4fab2014-01-10 22:24:11 +0000664 out_port = in_port;
Greg Clayton8b82f082011-04-12 05:54:46 +0000665 Error error;
666 // If we locate debugserver, keep that located version around
667 static FileSpec g_debugserver_file_spec;
668
Greg Clayton8b82f082011-04-12 05:54:46 +0000669 char debugserver_path[PATH_MAX];
670 FileSpec &debugserver_file_spec = launch_info.GetExecutableFile();
671
672 // Always check to see if we have an environment override for the path
673 // to the debugserver to use and use it if we do.
674 const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH");
675 if (env_debugserver_path)
Todd Fiala015d8182014-07-22 23:41:36 +0000676 {
Greg Clayton8b82f082011-04-12 05:54:46 +0000677 debugserver_file_spec.SetFile (env_debugserver_path, false);
Todd Fiala015d8182014-07-22 23:41:36 +0000678 if (log)
679 log->Printf ("GDBRemoteCommunication::%s() gdb-remote stub exe path set from environment variable: %s", __FUNCTION__, env_debugserver_path);
680 }
Greg Clayton8b82f082011-04-12 05:54:46 +0000681 else
682 debugserver_file_spec = g_debugserver_file_spec;
683 bool debugserver_exists = debugserver_file_spec.Exists();
684 if (!debugserver_exists)
685 {
686 // The debugserver binary is in the LLDB.framework/Resources
Zachary Turner42ff0ad2014-08-21 17:29:12 +0000687 // directory.
688 if (HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, debugserver_file_spec))
Greg Clayton8b82f082011-04-12 05:54:46 +0000689 {
Jason Molenda6fd86772014-08-21 23:22:33 +0000690 debugserver_file_spec.AppendPathComponent (DEBUGSERVER_BASENAME);
Greg Clayton8b82f082011-04-12 05:54:46 +0000691 debugserver_exists = debugserver_file_spec.Exists();
692 if (debugserver_exists)
693 {
Todd Fiala015d8182014-07-22 23:41:36 +0000694 if (log)
695 log->Printf ("GDBRemoteCommunication::%s() found gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ());
696
Greg Clayton8b82f082011-04-12 05:54:46 +0000697 g_debugserver_file_spec = debugserver_file_spec;
698 }
699 else
700 {
Todd Fiala015d8182014-07-22 23:41:36 +0000701 if (log)
702 log->Printf ("GDBRemoteCommunication::%s() could not find gdb-remote stub exe '%s'", __FUNCTION__, debugserver_file_spec.GetPath ().c_str ());
703
Greg Clayton8b82f082011-04-12 05:54:46 +0000704 g_debugserver_file_spec.Clear();
705 debugserver_file_spec.Clear();
706 }
707 }
708 }
709
710 if (debugserver_exists)
711 {
712 debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path));
713
714 Args &debugserver_args = launch_info.GetArguments();
715 debugserver_args.Clear();
716 char arg_cstr[PATH_MAX];
717
718 // Start args with "debugserver /file/path -r --"
719 debugserver_args.AppendArgument(debugserver_path);
Greg Clayton00fe87b2013-12-05 22:58:22 +0000720
721 // If a host and port is supplied then use it
Greg Claytonfda4fab2014-01-10 22:24:11 +0000722 char host_and_port[128];
723 if (hostname)
724 {
725 snprintf (host_and_port, sizeof(host_and_port), "%s:%u", hostname, in_port);
Greg Clayton00fe87b2013-12-05 22:58:22 +0000726 debugserver_args.AppendArgument(host_and_port);
Greg Claytonfda4fab2014-01-10 22:24:11 +0000727 }
728 else
729 {
730 host_and_port[0] = '\0';
731 }
732
Greg Clayton8b82f082011-04-12 05:54:46 +0000733 // use native registers, not the GDB registers
734 debugserver_args.AppendArgument("--native-regs");
735 // make debugserver run in its own session so signals generated by
736 // special terminal key sequences (^C) don't affect debugserver
737 debugserver_args.AppendArgument("--setsid");
Greg Clayton91a9b2472013-12-04 19:19:12 +0000738
Greg Clayton00fe87b2013-12-05 22:58:22 +0000739 char named_pipe_path[PATH_MAX];
Jason Molenda6e205542014-01-25 03:57:13 +0000740 named_pipe_path[0] = '\0';
Greg Clayton00fe87b2013-12-05 22:58:22 +0000741
Greg Claytonfda4fab2014-01-10 22:24:11 +0000742 bool listen = false;
743 if (host_and_port[0])
Greg Clayton91a9b2472013-12-04 19:19:12 +0000744 {
Greg Clayton00fe87b2013-12-05 22:58:22 +0000745 // Create a temporary file to get the stdout/stderr and redirect the
746 // output of the command into this file. We will later read this file
747 // if all goes well and fill the data into "command_output_ptr"
Greg Clayton00fe87b2013-12-05 22:58:22 +0000748
Greg Claytonfda4fab2014-01-10 22:24:11 +0000749 if (in_port == 0)
Greg Clayton00fe87b2013-12-05 22:58:22 +0000750 {
Greg Claytonfda4fab2014-01-10 22:24:11 +0000751 // Binding to port zero, we need to figure out what port it ends up
752 // using using a named pipe...
753 FileSpec tmpdir_file_spec;
Zachary Turner42ff0ad2014-08-21 17:29:12 +0000754 if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec))
Greg Clayton00fe87b2013-12-05 22:58:22 +0000755 {
Zachary Turnerc25146b2014-08-21 23:56:55 +0000756 tmpdir_file_spec.AppendPathComponent("debugserver-named-pipe.XXXXXX");
Greg Claytonfda4fab2014-01-10 22:24:11 +0000757 strncpy(named_pipe_path, tmpdir_file_spec.GetPath().c_str(), sizeof(named_pipe_path));
758 }
759 else
760 {
761 strncpy(named_pipe_path, "/tmp/debugserver-named-pipe.XXXXXX", sizeof(named_pipe_path));
762 }
763
764 if (::mktemp (named_pipe_path))
765 {
Hafiz Abid Qadeer6eff1012014-03-12 10:45:23 +0000766#if defined(_WIN32)
Deepak Panickalb36da432014-01-13 14:55:15 +0000767 if ( false )
768#else
Greg Claytonfda4fab2014-01-10 22:24:11 +0000769 if (::mkfifo(named_pipe_path, 0600) == 0)
Deepak Panickalb36da432014-01-13 14:55:15 +0000770#endif
Greg Claytonfda4fab2014-01-10 22:24:11 +0000771 {
772 debugserver_args.AppendArgument("--named-pipe");
773 debugserver_args.AppendArgument(named_pipe_path);
774 }
Greg Clayton00fe87b2013-12-05 22:58:22 +0000775 }
Greg Clayton91a9b2472013-12-04 19:19:12 +0000776 }
777 else
Greg Claytonfda4fab2014-01-10 22:24:11 +0000778 {
779 listen = true;
780 }
Greg Clayton91a9b2472013-12-04 19:19:12 +0000781 }
782 else
Greg Clayton00fe87b2013-12-05 22:58:22 +0000783 {
Greg Clayton00fe87b2013-12-05 22:58:22 +0000784 // No host and port given, so lets listen on our end and make the debugserver
785 // connect to us..
Greg Clayton16810922014-02-27 19:38:18 +0000786 error = StartListenThread ("127.0.0.1", 0);
Greg Clayton00fe87b2013-12-05 22:58:22 +0000787 if (error.Fail())
788 return error;
Greg Clayton8b82f082011-04-12 05:54:46 +0000789
Greg Clayton00fe87b2013-12-05 22:58:22 +0000790 ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection ();
Greg Clayton16810922014-02-27 19:38:18 +0000791 // Wait for 10 seconds to resolve the bound port
Zachary Turner98688922014-08-06 18:16:26 +0000792 out_port = connection->GetListeningPort(10);
Greg Clayton16810922014-02-27 19:38:18 +0000793 if (out_port > 0)
794 {
795 char port_cstr[32];
796 snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", out_port);
797 // Send the host and port down that debugserver and specify an option
798 // so that it connects back to the port we are listening to in this process
799 debugserver_args.AppendArgument("--reverse-connect");
800 debugserver_args.AppendArgument(port_cstr);
801 }
802 else
803 {
804 error.SetErrorString ("failed to bind to port 0 on 127.0.0.1");
805 return error;
806 }
Greg Clayton00fe87b2013-12-05 22:58:22 +0000807 }
Greg Clayton00fe87b2013-12-05 22:58:22 +0000808
Greg Clayton8b82f082011-04-12 05:54:46 +0000809 const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE");
810 if (env_debugserver_log_file)
811 {
812 ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file);
813 debugserver_args.AppendArgument(arg_cstr);
814 }
815
816 const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
817 if (env_debugserver_log_flags)
818 {
819 ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags);
820 debugserver_args.AppendArgument(arg_cstr);
821 }
Greg Clayton8b82f082011-04-12 05:54:46 +0000822
823 // Close STDIN, STDOUT and STDERR. We might need to redirect them
824 // to "/dev/null" if we run into any problems.
Greg Clayton91a9b2472013-12-04 19:19:12 +0000825 launch_info.AppendCloseFileAction (STDIN_FILENO);
826 launch_info.AppendCloseFileAction (STDOUT_FILENO);
827 launch_info.AppendCloseFileAction (STDERR_FILENO);
Greg Clayton8b82f082011-04-12 05:54:46 +0000828
829 error = Host::LaunchProcess(launch_info);
Greg Clayton91a9b2472013-12-04 19:19:12 +0000830
Greg Clayton3121fde2014-02-28 20:47:08 +0000831 if (error.Success() && launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
Greg Clayton91a9b2472013-12-04 19:19:12 +0000832 {
Greg Clayton3121fde2014-02-28 20:47:08 +0000833 if (named_pipe_path[0])
Greg Clayton91a9b2472013-12-04 19:19:12 +0000834 {
Greg Clayton3121fde2014-02-28 20:47:08 +0000835 File name_pipe_file;
836 error = name_pipe_file.Open(named_pipe_path, File::eOpenOptionRead);
837 if (error.Success())
838 {
839 char port_cstr[256];
840 port_cstr[0] = '\0';
841 size_t num_bytes = sizeof(port_cstr);
842 error = name_pipe_file.Read(port_cstr, num_bytes);
843 assert (error.Success());
844 assert (num_bytes > 0 && port_cstr[num_bytes-1] == '\0');
845 out_port = Args::StringToUInt32(port_cstr, 0);
846 name_pipe_file.Close();
847 }
Zachary Turnerc00cf4a2014-08-15 22:04:21 +0000848 FileSystem::Unlink(named_pipe_path);
Greg Clayton91a9b2472013-12-04 19:19:12 +0000849 }
Greg Clayton3121fde2014-02-28 20:47:08 +0000850 else if (listen)
851 {
852
853 }
854 else
855 {
856 // Make sure we actually connect with the debugserver...
857 JoinListenThread();
858 }
Greg Clayton00fe87b2013-12-05 22:58:22 +0000859 }
Greg Clayton8b82f082011-04-12 05:54:46 +0000860 }
861 else
862 {
Greg Clayton86edbf42011-10-26 00:56:27 +0000863 error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME );
Greg Clayton8b82f082011-04-12 05:54:46 +0000864 }
865 return error;
866}
867
Greg Claytonc1422c12012-04-09 22:46:21 +0000868void
Greg Claytond451c1a2012-04-13 21:24:18 +0000869GDBRemoteCommunication::DumpHistory(Stream &strm)
Greg Claytonc1422c12012-04-09 22:46:21 +0000870{
Greg Claytond451c1a2012-04-13 21:24:18 +0000871 m_history.Dump (strm);
Greg Claytonc1422c12012-04-09 22:46:21 +0000872}