├── README.md ├── example └── main.cpp ├── lib ├── evlog.cpp ├── evlog.h └── version.cpp └── php └── crash_report.php /README.md: -------------------------------------------------------------------------------- 1 | # Windows-Crash-Report-Cpp 2 | This C++11 library automatically grabs newly occurred Windows crash logs and sends the version/offset to an external online database for you to review. 3 | 4 | 5 | When a crash occurs on the user side, it is crucial for you to know the crash offset value in order to find the bug and provide support. 6 | 7 | There are several ways of achieving this. For instance, the user can click on the crash windows details button and copy the offset value for you. 8 | 9 | ![alt tag](https://cloud.githubusercontent.com/assets/10281739/10380884/f1a88c40-6e16-11e5-8f6b-bb3275ea03da.png) 10 | 11 | ![alt tag](https://cloud.githubusercontent.com/assets/10281739/10380889/f706f528-6e16-11e5-8dfa-7002ba57333c.png) 12 | 13 | In the case where the user closes the crash window, it is still possible to grab the crash log. The standard procedure is for the user to open the "Event Viewer" (Windows+R > eventvwr), look for the specific log in the list, open it and copy the offset value for you. 14 | 15 | ![alt tag](https://cloud.githubusercontent.com/assets/10281739/10380887/f6f04f62-6e16-11e5-9fd5-039f1fefb8b0.png) 16 | 17 | These two approaches involve manual control and are annoying for both users and developers. 18 | 19 | Our alternative approach consists of performing the crash lookup automatically and will save the crash report on your server. You'll need a server with SQL support. 20 | 21 | 1. Create a sql table: 22 | ![alt tag](https://cloud.githubusercontent.com/assets/10281739/10381038/1260d87e-6e18-11e5-838d-33e34e5d3211.png) 23 | 24 | 2. Upload the PHP script on your server. 25 | 26 | 3. Use the evlog lib (in the lib folder) and adapt your PHP script accordingly: 27 | ```C++ 28 | od::Http::callUrl("http://mysite.com/crash_report.php?version=" + std::to_string(err.version.value()) + "&error=" + std::to_string(err.offset), 3000); 29 | ``` 30 | 31 | All that's left for you is to look up the received crash offset value and find the origin of the bug in your application. To achieve this, using gcc, you can generate a .map file containing all functions/methods offsets: -Wl,-Map,file.map 32 | 33 | It is good practice to generate map files for each of your releases. 34 | 35 | 36 | MIT Licence 37 | ----------- 38 | 39 | Copyright (c) 2015 Marmontel Boris 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy 42 | of this software and associated documentation files (the "Software"), to deal 43 | in the Software without restriction, including without limitation the rights 44 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 45 | copies of the Software, and to permit persons to whom the Software is 46 | furnished to do so, subject to the following conditions: 47 | 48 | The above copyright notice and this permission notice shall be included in 49 | all copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 52 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 53 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 54 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 55 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 56 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 57 | THE SOFTWARE. 58 | -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "evlog.h" 3 | 4 | int main() 5 | { 6 | using namespace evlog; 7 | using namespace std; 8 | 9 | CrashReportWindows report("crash_logs.bin", "PlanetCentauri.exe"); 10 | report.parseLogs(20); // the 20 last logs 11 | 12 | cout << "Captured logs: " << report.logs().size() << '\n'; 13 | 14 | for(int i=0; i 3 | #include 4 | #include 5 | #include 6 | 7 | namespace evlog 8 | { 9 | 10 | bool Error::operator<(const Error& o) const 11 | { 12 | if(version == o.version) 13 | return offset < o.offset; 14 | 15 | return version < o.version; 16 | } 17 | 18 | CrashReportWindows::CrashReportWindows(const std::string& fname, const std::string& app_name): 19 | _fname(fname), 20 | _app_name(app_name) 21 | { 22 | // read already processed errors 23 | FILE * f = fopen(fname.c_str(), "rb"); 24 | if(f) 25 | { 26 | unsigned count, size; 27 | fread(&count, 1, sizeof(unsigned), f); 28 | fseek(f, 0, SEEK_END); 29 | size = ftell(f); 30 | fseek(f, 4, SEEK_SET); 31 | 32 | size = (size-sizeof(unsigned))/sizeof(unsigned)/2; 33 | 34 | if(count == size) 35 | { 36 | _processed.resize(count); 37 | for(unsigned i=0; i buffer(bytes_to_read); 90 | 91 | int ret = ReadEventLog(handle, EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ, i, &buffer[0], bytes_to_read, &bytes_read, &min_bytes_to_read); 92 | if(!ret) 93 | { 94 | return false; 95 | } 96 | unsigned count = dumpRecords(buffer); 97 | nb_processed += count; 98 | 99 | if(count == 0) 100 | i++; 101 | else 102 | i += count; 103 | 104 | if(nb_records_max != 0 && nb_processed >= nb_records_max) 105 | break; 106 | } 107 | 108 | CloseEventLog(handle); 109 | } 110 | else 111 | { 112 | return false; 113 | } 114 | return true; 115 | } 116 | 117 | unsigned CrashReportWindows::dumpRecords(const std::vector& buffer) 118 | { 119 | const char* ptr = &buffer[0]; 120 | const char* end = ptr + buffer.size(); 121 | 122 | auto readString = [&]() 123 | { 124 | std::string ret(ptr); 125 | ptr += ret.length(); 126 | 127 | while(*ptr == 0 && ptr != end) 128 | ptr++; 129 | 130 | return ret; 131 | }; 132 | 133 | std::set set(_processed.begin(), _processed.end()); 134 | 135 | unsigned count = 0; 136 | int meuh = 0; 137 | while(ptr < end) 138 | { 139 | if(strncmp("Application Error", ptr, 17) == 0) 140 | { 141 | const char* ptr2 = ptr - 40; 142 | ptr += 18; // Skip "Application Error" string 143 | 144 | Crash crash; 145 | crash.computer = readString(); 146 | crash.program = readString(); 147 | crash.version = readString(); 148 | crash.error_from = readString(); 149 | crash.version_dep = readString(); 150 | crash.offset = strtoul(readString().c_str(), nullptr, 16); 151 | 152 | std::string log = readString(); // on windows xp this is the log - on other the timestamp 153 | 154 | if(log.size() <= 10) // windows vista(?), 7, 8, 10 155 | { 156 | crash.timestamp = strtoul(log.c_str(), nullptr, 16); 157 | readString(); // Exception code 158 | crash.offset = strtoul(readString().c_str(), nullptr, 16); 159 | } 160 | else // win xp 161 | { 162 | crash.timestamp = ptr2[0]<<24 | ptr2[1]<<16 | ptr2[2]<<8 | ptr2[3]; 163 | } 164 | 165 | _logs.push_back(crash); 166 | 167 | Error err{crash.version, crash.offset}; 168 | if(crash.program == _app_name && set.find(err) == set.end()) 169 | { 170 | _new_errors.push_back(err); 171 | set.insert(err); 172 | } 173 | 174 | count++; 175 | } 176 | ptr++; 177 | } 178 | return count; 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /lib/evlog.h: -------------------------------------------------------------------------------- 1 | #ifndef EVLOG_H_INCLUDED 2 | #define EVLOG_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | namespace evlog 8 | { 9 | 10 | struct Version 11 | { 12 | unsigned char n[4]; 13 | 14 | public: 15 | Version(); 16 | Version(unsigned major, unsigned minor, unsigned build, unsigned revision); 17 | Version(unsigned value); 18 | Version(const std::string& str); 19 | 20 | unsigned value() const; 21 | void set(unsigned major, unsigned minor, unsigned build, unsigned revision); 22 | void set(unsigned value); 23 | void set(const std::string& str); 24 | 25 | unsigned char major() const; 26 | unsigned char minor() const; 27 | unsigned char build() const; 28 | unsigned char revision() const; 29 | 30 | unsigned char operator()(unsigned i) const; 31 | unsigned char& operator()(unsigned i); 32 | 33 | bool operator< (const Version& o) const; 34 | bool operator<=(const Version& o) const; 35 | bool operator> (const Version& o) const; 36 | bool operator>=(const Version& o) const; 37 | bool operator==(const Version& o) const; 38 | bool operator!=(const Version& o) const; 39 | 40 | std::string asString() const; 41 | }; 42 | 43 | struct Crash 44 | { 45 | std::string computer; 46 | std::string program; 47 | Version version; 48 | std::string error_from; 49 | Version version_dep; 50 | unsigned offset; 51 | unsigned timestamp; 52 | }; 53 | 54 | struct Error 55 | { 56 | Version version; 57 | unsigned offset; 58 | bool operator<(const Error& o) const; 59 | }; 60 | 61 | class CrashReportWindows 62 | { 63 | std::string _fname; 64 | std::string _app_name; 65 | 66 | std::vector _logs; 67 | 68 | std::vector _processed; 69 | std::vector _new_errors; 70 | 71 | unsigned dumpRecords(const std::vector& buffer); 72 | 73 | public: 74 | CrashReportWindows(const std::string& fname, const std::string& app_name); 75 | ~CrashReportWindows(); 76 | 77 | bool parseLogs(unsigned nb_records_max = 0); 78 | 79 | const std::vector& logs() const { return _logs; } 80 | const std::vector& newErrors() const { return _new_errors; } 81 | }; 82 | 83 | } 84 | 85 | #endif // EVLOG_H_INCLUDED 86 | -------------------------------------------------------------------------------- /lib/version.cpp: -------------------------------------------------------------------------------- 1 | #include "evlog.h" 2 | #include 3 | #include 4 | namespace evlog 5 | { 6 | 7 | Version::Version(): 8 | n{0, 0, 0, 0} 9 | { 10 | } 11 | Version::Version(unsigned major, unsigned minor, unsigned build, unsigned revision) 12 | { 13 | set(major, minor, build, revision); 14 | } 15 | Version::Version(unsigned value) 16 | { 17 | set(value); 18 | } 19 | Version::Version(const std::string& str) 20 | { 21 | set(str); 22 | } 23 | 24 | unsigned Version::value() const 25 | { 26 | return n[0] | n[1]<<8 | n[2]<<16 | n[3]<<24; 27 | } 28 | 29 | void Version::set(unsigned major, unsigned minor, unsigned build, unsigned revision) 30 | { 31 | n[0] = major>255? 255:major; 32 | n[1] = minor>255? 255:minor; 33 | n[2] = build>255? 255:build; 34 | n[3] = revision>255? 255:revision; 35 | } 36 | void Version::set(unsigned value) 37 | { 38 | n[0] = value & 0xFF; 39 | n[1] = (value >> 8) & 0xFF; 40 | n[2] = (value >> 16) & 0xFF; 41 | n[3] = (value >> 24) & 0xFF; 42 | } 43 | void Version::set(const std::string& str) 44 | { 45 | unsigned i=0; 46 | int x=0; 47 | for(unsigned j=0; j= '0' && c <= '9') 56 | { 57 | x = x*10 + c - '0'; 58 | } 59 | } 60 | n[i] = x; 61 | for(++i; i<4; ++i) 62 | n[i] = 0; 63 | } 64 | 65 | unsigned char Version::major() const 66 | { 67 | return n[0]; 68 | } 69 | unsigned char Version::minor() const 70 | { 71 | return n[1]; 72 | } 73 | unsigned char Version::build() const 74 | { 75 | return n[2]; 76 | } 77 | unsigned char Version::revision() const 78 | { 79 | return n[3]; 80 | } 81 | 82 | unsigned char Version::operator()(unsigned i) const 83 | { 84 | assert(i < 4); 85 | return n[i]; 86 | } 87 | 88 | unsigned char& Version::operator()(unsigned i) 89 | { 90 | assert(i < 4); 91 | return n[i]; 92 | } 93 | 94 | bool Version::operator< (const Version& o) const 95 | { 96 | for(int i=0; i<4; ++i) 97 | { 98 | if(n[i] < o.n[i]) 99 | return true; 100 | if(n[i] > o.n[i]) 101 | return false; 102 | } 103 | return false; 104 | } 105 | bool Version::operator<=(const Version& o) const 106 | { 107 | return *this (const Version& o) const 110 | { 111 | return !(*this<=o); 112 | } 113 | bool Version::operator>=(const Version& o) const 114 | { 115 | return !(*thisconnect_error) 24 | die ($mysqli->connect_error); 25 | 26 | // Check if crash adress exists 27 | if($ans = $mysqli->query("select count from ".$table." where error=".$error.";")) 28 | { 29 | $datarow = $ans->fetch_array(); 30 | $count = $datarow[0]; 31 | 32 | if($count == NULL) 33 | { 34 | $count = 1; 35 | } 36 | else 37 | { 38 | $count++; 39 | $mysqli->query("delete from ".$table." where error=".$error.";"); 40 | } 41 | } 42 | 43 | // Insert crash report in database 44 | $mysqli->query("insert into ".$table." VALUES (".$version.", '".$error."', ".$count.");"); 45 | $mysqli->close(); 46 | 47 | ?> 48 | --------------------------------------------------------------------------------