diff options
Diffstat (limited to 'lldb/tools/debugserver/source/RNBRemote.cpp')
-rw-r--r-- | lldb/tools/debugserver/source/RNBRemote.cpp | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index ccc0a9ceaa5..e8fb114d2ba 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -28,9 +28,11 @@ #include "RNBServices.h" #include "RNBSocket.h" #include "Utility/StringExtractor.h" +#include "MacOSX/Genealogy.h" #include <iomanip> #include <sstream> +#include <unordered_set> #include <TargetConditionals.h> // for endianness predefines //---------------------------------------------------------------------- @@ -175,6 +177,7 @@ RNBRemote::CreatePacketTable () t.push_back (Packet (query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion, NULL, "qGDBServerVersion", "Replies with multiple 'key:value;' tuples appended to each other.")); t.push_back (Packet (query_process_info, &RNBRemote::HandlePacket_qProcessInfo, NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); // t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qSymbol", "Notify that host debugger is ready to do symbol lookups")); + t.push_back (Packet (json_query_thread_extended_info, &RNBRemote::HandlePacket_jThreadExtendedInfo, NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information.")); t.push_back (Packet (start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets")); t.push_back (Packet (prefix_reg_packets_with_tid, &RNBRemote::HandlePacket_QThreadSuffixSupported , NULL, "QThreadSuffixSupported", "Check if thread specifc packets (register packets 'g', 'G', 'p', and 'P') support having the thread ID appended to the end of the command")); t.push_back (Packet (set_logging_mode, &RNBRemote::HandlePacket_QSetLogging , NULL, "QSetLogging:", "Check if register packets ('g', 'G', 'p', and 'P' support having the thread ID prefix")); @@ -850,6 +853,57 @@ decode_binary_data (const char *str, size_t len) return bytes; } +// Quote any meta characters in a std::string as per the binary +// packet convention in the gdb-remote protocol. + +std::string +binary_encode_string (const std::string &s) +{ + std::string output; + const size_t s_size = s.size(); + const char *s_chars = s.c_str(); + + for (size_t i = 0; i < s_size; i++) + { + unsigned char ch = *(s_chars + i); + if (ch == '#' || ch == '$' || ch == '}' || ch == '*') + { + output.push_back ('}'); // 0x7d + output.push_back (ch ^ 0x20); + } + else + { + output.push_back (ch); + } + } + return output; +} + +// If the value side of a key-value pair in JSON is a string, +// and that string has a " character in it, the " character must +// be escaped. + +std::string +json_string_quote_metachars (const std::string &s) +{ + if (s.find('"') == std::string::npos) + return s; + + std::string output; + const size_t s_size = s.size(); + const char *s_chars = s.c_str(); + for (size_t i = 0; i < s_size; i++) + { + unsigned char ch = *(s_chars + i); + if (ch == '"') + { + output.push_back ('\\'); + } + output.push_back (ch); + } + return output; +} + typedef struct register_map_entry { uint32_t gdb_regnum; // gdb register number @@ -2135,6 +2189,18 @@ append_hex_value (std::ostream& ostrm, const uint8_t* buf, size_t buf_size, bool } } +void +append_hexified_string (std::ostream& ostrm, const std::string &string) +{ + size_t string_size = string.size(); + const char *string_buf = string.c_str(); + for (size_t i = 0; i < string_size; i++) + { + ostrm << RAWHEX8(*(string_buf + i)); + } +} + + void register_value_in_hex_fixed_width (std::ostream& ostrm, @@ -3963,6 +4029,274 @@ RNBRemote::HandlePacket_qGDBServerVersion (const char *p) return SendPacket (strm.str()); } +// A helper function that retrieves a single integer value from +// a one-level-deep JSON dictionary of key-value pairs. e.g. +// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}] +// +uint64_t +get_integer_value_for_key_name_from_json (const char *key, const char *json_string) +{ + uint64_t retval = INVALID_NUB_ADDRESS; + std::string key_with_quotes = "\""; + key_with_quotes += key; + key_with_quotes += "\":"; + const char *c = strstr (json_string, key_with_quotes.c_str()); + if (c) + { + c += key_with_quotes.size(); + errno = 0; + retval = strtoul (c, NULL, 10); + if (errno != 0) + { + retval = INVALID_NUB_ADDRESS; + } + } + return retval; + +} + +rnb_err_t +RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p) +{ + nub_process_t pid; + std::ostringstream json; + std::ostringstream reply_strm; + // If we haven't run the process yet, return an error. + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E81"); + } + + pid = m_ctx.ProcessID(); + + const char thread_extended_info_str[] = { "jThreadExtendedInfo:{" }; + if (strncmp (p, thread_extended_info_str, sizeof (thread_extended_info_str) - 1) == 0) + { + p += strlen (thread_extended_info_str); + + uint64_t tid = get_integer_value_for_key_name_from_json ("thread", p); + uint64_t plo_pthread_tsd_base_address_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_address_offset", p); + uint64_t plo_pthread_tsd_base_offset = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_base_offset", p); + uint64_t plo_pthread_tsd_entry_size = get_integer_value_for_key_name_from_json ("plo_pthread_tsd_entry_size", p); + uint64_t dti_qos_class_index = get_integer_value_for_key_name_from_json ("dti_qos_class_index", p); + // Commented out the two variables below as they are not being used +// uint64_t dti_queue_index = get_integer_value_for_key_name_from_json ("dti_queue_index", p); +// uint64_t dti_voucher_index = get_integer_value_for_key_name_from_json ("dti_voucher_index", p); + + if (tid != INVALID_NUB_ADDRESS) + { + nub_addr_t pthread_t_value = DNBGetPThreadT (pid, tid); + + uint64_t tsd_address = INVALID_NUB_ADDRESS; + if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS + && plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS + && plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS) + { + tsd_address = DNBGetTSDAddressForThread (pid, tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); + } + + bool timed_out = false; + Genealogy::ThreadActivitySP thread_activity_sp; + + // If the pthread_t value is invalid, or if we were able to fetch the thread's TSD base + // and got an invalid value back, then we have a thread in early startup or shutdown and + // it's possible that gathering the genealogy information for this thread go badly. + // Ideally fetching this info for a thread in these odd states shouldn't matter - but + // we've seen some problems with these new SPI and threads in edge-casey states. + + double genealogy_fetch_time = 0; + if (pthread_t_value != INVALID_NUB_ADDRESS && tsd_address != INVALID_NUB_ADDRESS) + { + DNBTimer timer(false); + thread_activity_sp = DNBGetGenealogyInfoForThread (pid, tid, timed_out); + genealogy_fetch_time = timer.ElapsedMicroSeconds(false) / 1000000.0; + } + + std::unordered_set<uint32_t> process_info_indexes; // an array of the process info #'s seen + + json << "{"; + + bool need_to_print_comma = false; + + if (thread_activity_sp && timed_out == false) + { + const Genealogy::Activity *activity = &thread_activity_sp->current_activity; + bool need_vouchers_comma_sep = false; + json << "\"activity_query_timed_out\":false,"; + if (genealogy_fetch_time != 0) + { + // If we append the floating point value with << we'll get it in scientific + // notation. + char floating_point_ascii_buffer[64]; + floating_point_ascii_buffer[0] = '\0'; + snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time); + if (strlen (floating_point_ascii_buffer) > 0) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"activity_query_duration\":" << floating_point_ascii_buffer; + } + } + if (activity->activity_id != 0) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + need_vouchers_comma_sep = true; + json << "\"activity\":{"; + json << "\"start\":" << activity->activity_start << ","; + json << "\"id\":" << activity->activity_id << ","; + json << "\"parent_id\":" << activity->parent_id << ","; + json << "\"name\":\"" << json_string_quote_metachars (activity->activity_name) << "\","; + json << "\"reason\":\"" << json_string_quote_metachars (activity->reason) << "\""; + json << "}"; + } + if (thread_activity_sp->messages.size() > 0) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"trace_messages\":["; + bool printed_one_message = false; + for (auto iter = thread_activity_sp->messages.begin() ; iter != thread_activity_sp->messages.end(); ++iter) + { + if (printed_one_message) + json << ","; + else + printed_one_message = true; + json << "{"; + json << "\"timestamp\":" << iter->timestamp << ","; + json << "\"activity_id\":" << iter->activity_id << ","; + json << "\"trace_id\":" << iter->trace_id << ","; + json << "\"thread\":" << iter->thread << ","; + json << "\"type\":" << (int) iter->type << ","; + json << "\"process_info_index\":" << iter->process_info_index << ","; + process_info_indexes.insert (iter->process_info_index); + json << "\"message\":\"" << json_string_quote_metachars (iter->message) << "\""; + json << "}"; + } + json << "]"; + } + if (thread_activity_sp->breadcrumbs.size() == 1) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"breadcrumb\":{"; + for (auto iter = thread_activity_sp->breadcrumbs.begin() ; iter != thread_activity_sp->breadcrumbs.end(); ++iter) + { + json << "\"breadcrumb_id\":" << iter->breadcrumb_id << ","; + json << "\"activity_id\":" << iter->activity_id << ","; + json << "\"timestamp\":" << iter->timestamp << ","; + json << "\"name\":\"" << json_string_quote_metachars (iter->name) << "\""; + } + json << "}"; + } + if (process_info_indexes.size() > 0) + { + need_to_print_comma = true; + if (need_vouchers_comma_sep) + json << ","; + need_vouchers_comma_sep = true; + json << "\"process_infos\":["; + bool printed_one_process_info = false; + for (auto iter = process_info_indexes.begin(); iter != process_info_indexes.end(); ++iter) + { + if (printed_one_process_info) + json << ","; + else + printed_one_process_info = true; + Genealogy::ProcessExecutableInfoSP image_info_sp; + uint32_t idx = *iter; + image_info_sp = DNBGetGenealogyImageInfo (pid, idx); + json << "{"; + char uuid_buf[37]; + uuid_unparse_upper (image_info_sp->image_uuid, uuid_buf); + json << "\"process_info_index\":" << idx << ","; + json << "\"image_path\":\"" << json_string_quote_metachars (image_info_sp->image_path) << "\","; + json << "\"image_uuid\":\"" << uuid_buf <<"\""; + json << "}"; + } + json << "]"; + } + } + else + { + if (timed_out) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"activity_query_timed_out\":true"; + if (genealogy_fetch_time != 0) + { + // If we append the floating point value with << we'll get it in scientific + // notation. + char floating_point_ascii_buffer[64]; + floating_point_ascii_buffer[0] = '\0'; + snprintf (floating_point_ascii_buffer, sizeof (floating_point_ascii_buffer), "%f", genealogy_fetch_time); + if (strlen (floating_point_ascii_buffer) > 0) + { + json << ","; + json << "\"activity_query_duration\":" << floating_point_ascii_buffer; + } + } + } + } + + if (tsd_address != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"tsd_address\":" << tsd_address; + + if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX) + { + ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread (pid, tid, tsd_address, dti_qos_class_index); + if (requested_qos.IsValid()) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"requested_qos\":{"; + json << "\"enum_value\":" << requested_qos.enum_value << ","; + json << "\"constant_name\":\"" << json_string_quote_metachars (requested_qos.constant_name) << "\","; + json << "\"printable_name\":\"" << json_string_quote_metachars (requested_qos.printable_name) << "\""; + json << "}"; + } + } + } + + if (pthread_t_value != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"pthread_t\":" << pthread_t_value; + } + + nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT (pid, tid); + if (dispatch_queue_t_value != INVALID_NUB_ADDRESS) + { + if (need_to_print_comma) + json << ","; + need_to_print_comma = true; + json << "\"dispatch_queue_t\":" << dispatch_queue_t_value; + } + + json << "}"; + std::string json_quoted = binary_encode_string (json.str()); + reply_strm << json_quoted; + return SendPacket (reply_strm.str()); + } + } + return SendPacket ("OK"); +} + // Note that all numeric values returned by qProcessInfo are hex encoded, // including the pid and the cpu type. |