diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/windows/pdb_source_line_writer.cc | 124 | ||||
-rw-r--r-- | src/common/windows/pdb_source_line_writer.h | 32 | ||||
-rw-r--r-- | src/common/windows/string_utils-inl.h | 3 | ||||
-rw-r--r-- | src/common/windows/string_utils.cc | 59 |
4 files changed, 204 insertions, 14 deletions
diff --git a/src/common/windows/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc index 48a35bed..9969530b 100644 --- a/src/common/windows/pdb_source_line_writer.cc +++ b/src/common/windows/pdb_source_line_writer.cc @@ -28,8 +28,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <atlbase.h> -#include <DbgHelp.h> #include <dia2.h> +#include <ImageHlp.h> #include <stdio.h> #include "common/windows/string_utils-inl.h" @@ -45,6 +45,24 @@ namespace google_breakpad { +using std::vector; + +// A helper class to scope a PLOADED_IMAGE. +class AutoImage { + public: + explicit AutoImage(PLOADED_IMAGE img) : img_(img) {} + ~AutoImage() { + if (img_) + ImageUnload(img_); + } + + operator PLOADED_IMAGE() { return img_; } + PLOADED_IMAGE operator->() { return img_; } + + private: + PLOADED_IMAGE img_; +}; + PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { } @@ -78,6 +96,7 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { fprintf(stderr, "loadDataForExe failed\n"); return false; } + code_file_ = file; break; case ANY_FILE: if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { @@ -85,6 +104,7 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { fprintf(stderr, "loadDataForPdb and loadDataFromExe failed\n"); return false; } + code_file_ = file; } break; default: @@ -498,6 +518,18 @@ bool PDBSourceLineWriter::PrintPDBInfo() { return true; } +bool PDBSourceLineWriter::PrintPEInfo() { + PEModuleInfo info; + if (!GetPEInfo(&info)) { + return false; + } + + fprintf(output_, "INFO CODE_ID %ws %ws\n", + info.code_identifier.c_str(), + info.code_file.c_str()); + return true; +} + // wcstol_positive_strict is sort of like wcstol, but much stricter. string // should be a buffer pointing to a null-terminated string containing only // decimal digits. If the entire string can be converted to an integer @@ -535,6 +567,35 @@ static bool wcstol_positive_strict(wchar_t *string, int *result) { return true; } +bool PDBSourceLineWriter::FindPEFile() { + CComPtr<IDiaSymbol> global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComBSTR symbols_file; + if (SUCCEEDED(global->get_symbolsFileName(&symbols_file))) { + wstring file(symbols_file); + + // Look for an EXE or DLL file. + const wchar_t *extensions[] = { L"exe", L"dll" }; + for (int i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) { + size_t dot_pos = file.find_last_of(L"."); + if (dot_pos != wstring::npos) { + file.replace(dot_pos + 1, wstring::npos, extensions[i]); + // Check if this file exists. + if (GetFileAttributesW(file.c_str()) != INVALID_FILE_ATTRIBUTES) { + code_file_ = file; + return true; + } + } + } + } + + return false; +} + // static bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, BSTR *name, @@ -737,10 +798,15 @@ next_child: bool PDBSourceLineWriter::WriteMap(FILE *map_file) { output_ = map_file; - bool ret = PrintPDBInfo() && - PrintSourceFiles() && - PrintFunctions() && - PrintFrameData(); + bool ret = PrintPDBInfo(); + // TODO(ted): This is currently disabled to allow Breakpad users to update + // processing infrastructure. + // This is not a critical piece of the symbol file. + // PrintPEInfo(); + ret = ret && + PrintSourceFiles() && + PrintFunctions() && + PrintFrameData(); output_ = NULL; return ret; @@ -846,6 +912,54 @@ bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { return true; } +bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) { + if (!info) { + return false; + } + + if (code_file_.empty() && !FindPEFile()) { + fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); + return false; + } + + // Convert wchar to native charset because ImageLoad only takes + // a PSTR as input. + string code_file; + if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { + return false; + } + + AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); + if (!img) { + fprintf(stderr, "Failed to open PE file: %s\n", code_file.c_str()); + return false; + } + + info->code_file = WindowsStringUtils::GetBaseName(code_file_); + + // The date and time that the file was created by the linker. + DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp; + // The size of the file in bytes, including all headers. + DWORD SizeOfImage = 0; + PIMAGE_OPTIONAL_HEADER64 opt = + &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader; + if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + // 64-bit PE file. + SizeOfImage = opt->SizeOfImage; + } + else { + // 32-bit PE file. + SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage; + } + wchar_t code_identifier[32]; + swprintf(code_identifier, + sizeof(code_identifier) / sizeof(code_identifier[0]), + L"%08X%X", TimeDateStamp, SizeOfImage); + info->code_identifier = code_identifier; + + return true; +} + bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) { if (!uses_guid) return false; diff --git a/src/common/windows/pdb_source_line_writer.h b/src/common/windows/pdb_source_line_writer.h index 8524d8b0..ef2a27db 100644 --- a/src/common/windows/pdb_source_line_writer.h +++ b/src/common/windows/pdb_source_line_writer.h @@ -67,6 +67,21 @@ struct PDBModuleInfo { wstring cpu; }; +// A structure that carries information that identifies a PE file, +// either an EXE or a DLL. +struct PEModuleInfo { + // The basename of the PE file. + wstring code_file; + + // The PE file's code identifier, which consists of its timestamp + // and file size concatenated together into a single hex string. + // (The fields IMAGE_OPTIONAL_HEADER::SizeOfImage and + // IMAGE_FILE_HEADER::TimeDateStamp, as defined in the ImageHlp + // documentation.) This is not well documented, if it's documented + // at all, but it's what symstore does and what DbgHelp supports. + wstring code_identifier; +}; + class PDBSourceLineWriter { public: enum FileFormat { @@ -100,6 +115,10 @@ class PDBSourceLineWriter { // true on success and false on failure. bool GetModuleInfo(PDBModuleInfo *info); + // Retrieves information about the module's PE file. Returns + // true on success and false on failure. + bool GetPEInfo(PEModuleInfo *info); + // Sets uses_guid to true if the opened file uses a new-style CodeView // record with a 128-bit GUID, or false if the opened file uses an old-style // CodeView record. When no GUID is available, a 32-bit signature should be @@ -139,6 +158,11 @@ class PDBSourceLineWriter { // its uuid and age. bool PrintPDBInfo(); + // Outputs a line identifying the PE file corresponding to the PDB + // file that is being dumped, along with its code identifier, + // which consists of its timestamp and file size. + bool PrintPEInfo(); + // Returns true if this filename has already been seen, // and an ID is stored for it, or false if it has not. bool FileIDIsCached(const wstring &file) { @@ -170,6 +194,10 @@ class PDBSourceLineWriter { return iter->second; }; + // Find the PE file corresponding to the loaded PDB file, and + // set the code_file_ member. Returns false on failure. + bool FindPEFile(); + // Returns the function name for a symbol. If possible, the name is // undecorated. If the symbol's decorated form indicates the size of // parameters on the stack, this information is returned in stack_param_size. @@ -183,6 +211,10 @@ class PDBSourceLineWriter { // a failure, returns 0, which is also a valid number of bytes. static int GetFunctionStackParamSize(IDiaSymbol *function); + // The filename of the PE file corresponding to the currently-open + // pdb file. + wstring code_file_; + // The session for the currently-open pdb file. CComPtr<IDiaSession> session_; diff --git a/src/common/windows/string_utils-inl.h b/src/common/windows/string_utils-inl.h index 6f650812..d281aaa1 100644 --- a/src/common/windows/string_utils-inl.h +++ b/src/common/windows/string_utils-inl.h @@ -87,6 +87,9 @@ class WindowsStringUtils { // without setting wcs. static bool safe_mbstowcs(const string &mbs, wstring *wcs); + // The inverse of safe_mbstowcs. + static bool safe_wcstombs(const wstring &wcs, string *mbs); + // Returns the base name of a file, e.g. strips off the path. static wstring GetBaseName(const wstring &filename); diff --git a/src/common/windows/string_utils.cc b/src/common/windows/string_utils.cc index 5d464802..e6ffa082 100644 --- a/src/common/windows/string_utils.cc +++ b/src/common/windows/string_utils.cc @@ -28,6 +28,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <cassert> +#include <vector> #include "common/windows/string_utils-inl.h" @@ -55,6 +56,7 @@ bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) { if ((err = mbstowcs_s(&wcs_length, NULL, 0, mbs.c_str(), _TRUNCATE)) != 0) { return false; } + assert(wcs_length > 0); #else // _MSC_VER >= 1400 if ((wcs_length = mbstowcs(NULL, mbs.c_str(), mbs.length())) < 0) { return false; @@ -64,28 +66,67 @@ bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) { ++wcs_length; #endif // _MSC_VER >= 1400 - // TODO(mmentovai): move scoped_ptr into common and use it for wcs_c. - wchar_t *wcs_c = new wchar_t[wcs_length]; + std::vector<wchar_t> wcs_v(wcs_length); // Now, convert. #if _MSC_VER >= 1400 // MSVC 2005/8 - if ((err = mbstowcs_s(NULL, wcs_c, wcs_length, mbs.c_str(), + if ((err = mbstowcs_s(NULL, &wcs_v[0], wcs_length, mbs.c_str(), _TRUNCATE)) != 0) { - delete[] wcs_c; return false; } #else // _MSC_VER >= 1400 - if (mbstowcs(wcs_c, mbs.c_str(), mbs.length()) < 0) { - delete[] wcs_c; + if (mbstowcs(&wcs_v[0], mbs.c_str(), mbs.length()) < 0) { return false; } // Ensure presence of 0-terminator. - wcs_c[wcs_length - 1] = '\0'; + wcs_v[wcs_length - 1] = '\0'; #endif // _MSC_VER >= 1400 - *wcs = wcs_c; - delete[] wcs_c; + *wcs = &wcs_v[0]; + return true; +} + +// static +bool WindowsStringUtils::safe_wcstombs(const wstring &wcs, string *mbs) { + assert(mbs); + + // First, determine the length of the destination buffer. + size_t mbs_length; + +#if _MSC_VER >= 1400 // MSVC 2005/8 + errno_t err; + if ((err = wcstombs_s(&mbs_length, NULL, 0, wcs.c_str(), _TRUNCATE)) != 0) { + return false; + } + assert(mbs_length > 0); +#else // _MSC_VER >= 1400 + if ((mbs_length = wcstombs(NULL, wcs.c_str(), wcs.length())) < 0) { + return false; + } + + // Leave space for the 0-terminator. + ++mbs_length; +#endif // _MSC_VER >= 1400 + + std::vector<char> mbs_v(mbs_length); + + // Now, convert. +#if _MSC_VER >= 1400 // MSVC 2005/8 + if ((err = wcstombs_s(NULL, &mbs_v[0], mbs_length, wcs.c_str(), + _TRUNCATE)) != 0) { + return false; + } +#else // _MSC_VER >= 1400 + if (wcstombs(&mbs_v[0], wcs.c_str(), wcs.length()) < 0) { + return false; + } + + // Ensure presence of 0-terminator. + mbs_v[mbs_length - 1] = '\0'; +#endif // _MSC_VER >= 1400 + + *mbs = &mbs_v[0]; return true; } |