/******************************************************************************* | |
* Copyright (c) 2009 Nokia and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Nokia - Initial API and implementation | |
*******************************************************************************/ | |
#include <string> | |
#include <vector> | |
#include <algorithm> | |
#include "SettingsService.h" | |
#include "TCFChannel.h" | |
#include "WinDebugMonitor.h" | |
#define _EXCEPTION_ACCESS_VIOLATION 1 << 0; | |
#define _EXCEPTION_ARRAY_BOUNDS_EXCEEDED 1 << 1; | |
#define _EXCEPTION_DATATYPE_MISALIGNMENT 1 << 2; | |
#define _EXCEPTION_FLT_DENORMAL_OPERAND 1 << 3; | |
#define _EXCEPTION_FLT_DIVIDE_BY_ZERO 1 << 4; | |
#define _EXCEPTION_FLT_INEXACT_RESULT 1 << 5; | |
#define _EXCEPTION_FLT_INVALID_OPERATION 1 << 6; | |
#define _EXCEPTION_FLT_OVERFLOW 1 << 7; | |
#define _EXCEPTION_FLT_STACK_CHECK 1 << 8; | |
#define _EXCEPTION_FLT_UNDERFLOW 1 << 9; | |
#define _EXCEPTION_ILLEGAL_INSTRUCTION 1 << 10; | |
#define _EXCEPTION_IN_PAGE_ERROR 1 << 11; | |
#define _EXCEPTION_INT_DIVIDE_BY_ZERO 1 << 12; | |
#define _EXCEPTION_INT_OVERFLOW 1 << 13; | |
#define _EXCEPTION_INVALID_DISPOSITION 1 << 14; | |
#define _EXCEPTION_NONCONTINUABLE_EXCEPTION 1 << 15; | |
#define _EXCEPTION_PRIV_INSTRUCTION 1 << 16; | |
#define _EXCEPTION_STACK_OVERFLOW 1 << 17; | |
#define _EXCEPTION_GUARD_PAGE 1 << 18; | |
#define _EXCEPTION_INVALID_HANDLE 1 << 19; | |
#define _DBG_CONTROL_C 1 << 20; | |
#define _DBG_CONTROL_BREAK 1 << 21; | |
#define _STATUS_NO_MEMORY 1 << 22; | |
#define _STATUS_DLL_INIT_FAILED 1 << 23; | |
#define _STATUS_DLL_NOT_FOUND 1 << 24; | |
#define _STATUS_ENTRYPOINT_NOT_FOUND 1 << 25; | |
#define _MS_CPLUS_EXCEPTION 1 << 26; | |
static const char * sServiceName = "Settings"; | |
static std::set<std::string> modulesToDebug; | |
static bool allowFilenameMatch; | |
static bool enableDebugStringLogging; | |
static unsigned long exceptionsBitmask; | |
static std::vector<std::string> settingIdArray; | |
static std::vector<std::string> executables; | |
SettingsService::SettingsService(Protocol * proto) : | |
TCFService(proto) { | |
allowFilenameMatch = false; | |
enableDebugStringLogging = true; | |
exceptionsBitmask = 0x00000000L; // no exceptions reported by default | |
AddCommand("get", command_get_settings); // compatibility -- not used | |
AddCommand("getIds", command_get_settings); | |
AddCommand("set", command_set_settings); | |
} | |
SettingsService::~SettingsService(void) { | |
} | |
const char* SettingsService::GetName() { | |
return sServiceName; | |
} | |
void SettingsService::command_get_settings(const char * token, Channel * c) { | |
TCFChannel channel(c); | |
channel.readComplete(); | |
channel.writeReplyHeader(token); | |
channel.writeError(0); | |
write_stream(&c->out,'['); | |
write_stream(&c->out,']'); | |
channel.writeZero(); | |
channel.writeComplete(); | |
} | |
static void read_setting_id(InputStream * inp, void * arg) | |
{ | |
Channel* c = static_cast<Channel*>(arg); | |
TCFChannel channel(c); | |
std::string id = channel.readString(); | |
settingIdArray.push_back(id); | |
} | |
static void read_executable(InputStream * inp, void * arg) | |
{ | |
Channel* c = static_cast<Channel*>(arg); | |
TCFChannel channel(c); | |
std::string executable = channel.readString(); | |
// convert to lower case for later case-insensitive comparison (windows is case-insensitive) | |
std::transform(executable.begin(), executable.end(), executable.begin(), ::tolower); | |
modulesToDebug.insert(executable); | |
} | |
static void read_setting_value(InputStream * inp, void * arg) | |
{ | |
Channel* c = static_cast<Channel*>(arg); | |
TCFChannel channel(c); | |
// the setting ids are stored in settingIdArray in the same | |
// order as this function gets called with the values. so just | |
// pop the first one off each time to get the setting id for this value. | |
std::string id = settingIdArray.front(); | |
settingIdArray.erase(settingIdArray.begin()); | |
if (id.compare("addModules") == 0) | |
json_read_array(inp, read_executable, c); | |
else if (id.compare("allowFilenameMatch") == 0) | |
allowFilenameMatch = json_read_boolean(inp); | |
else if (id.compare("enableDebugStringLogging") == 0) | |
enableDebugStringLogging = json_read_boolean(inp); | |
else if (id.compare("exceptionsBitmask") == 0) | |
exceptionsBitmask = json_read_ulong(inp); | |
else { | |
// skip unknown setting | |
json_skip_object(&c->inp); | |
} | |
} | |
void SettingsService::command_set_settings(const char * token, Channel * c) { | |
TCFChannel channel(c); | |
std::string str = channel.readString(); // ignored | |
channel.readZero(); | |
settingIdArray.clear(); | |
// read the array of setting ids | |
json_read_array(&c->inp, read_setting_id, c); | |
channel.readZero(); | |
// now read the array of setting values | |
json_read_array(&c->inp, read_setting_value, c); | |
channel.readZero(); | |
channel.readComplete(); | |
channel.writeReplyHeader(token); | |
channel.writeError(0); | |
channel.writeComplete(); | |
} | |
std::string getFilenameFromPath(std::string path) | |
{ | |
std::string::size_type pos = path.find_last_of('\\'); | |
if (pos == std::string::npos) | |
return NULL; | |
return path.substr(pos + 1, path.size() - pos - 1); | |
} | |
/* | |
* Called when debug session ends. Do any cleanup needed. | |
*/ | |
void SettingsService::debugSessionEnds() | |
{ | |
modulesToDebug.clear(); | |
} | |
bool SettingsService::reportDebugEventForModule(std::string module) | |
{ | |
// if modulesToDebug is empty then no filter has been set - report all events | |
if (modulesToDebug.empty()) | |
return true; | |
// first convert to lower case - windows is case-insensitive after all | |
std::string moduleLower(module); | |
std::transform(moduleLower.begin(), moduleLower.end(), moduleLower.begin(), ::tolower); | |
// first look for an exact, full path match | |
if (modulesToDebug.count(moduleLower) > 0) | |
return true; | |
if (allowFilenameMatch) | |
{ | |
// now just see if the filenames of any of the modules in the filter match the given module | |
std::set<std::string>::iterator itModules; | |
for (itModules = modulesToDebug.begin(); itModules | |
!= modulesToDebug.end(); itModules++) | |
{ | |
std::string path = *itModules; | |
// get the filename | |
std::string filename = getFilenameFromPath(path); | |
// search the module string | |
if (std::string::npos != moduleLower.rfind(filename)) { | |
return true; | |
} | |
} | |
} | |
// filter is set but the given module is not in the list - don't report | |
return false; | |
} | |
bool SettingsService::reportDebugStringEvents() | |
{ | |
return enableDebugStringLogging; | |
} | |
bool SettingsService::reportExceptionEvent(const DEBUG_EVENT& debugEvent) | |
{ | |
switch (debugEvent.u.Exception.ExceptionRecord.ExceptionCode) { | |
// as a debugger we always report these | |
case EXCEPTION_SINGLE_STEP: | |
case EXCEPTION_BREAKPOINT: | |
return true; | |
// honor the exception settings | |
case EXCEPTION_ACCESS_VIOLATION: | |
return exceptionsBitmask & _EXCEPTION_ACCESS_VIOLATION; | |
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: | |
return exceptionsBitmask & _EXCEPTION_ARRAY_BOUNDS_EXCEEDED; | |
case EXCEPTION_DATATYPE_MISALIGNMENT: | |
return exceptionsBitmask & _EXCEPTION_DATATYPE_MISALIGNMENT; | |
case EXCEPTION_FLT_DENORMAL_OPERAND: | |
return exceptionsBitmask & _EXCEPTION_FLT_DENORMAL_OPERAND; | |
case EXCEPTION_FLT_DIVIDE_BY_ZERO: | |
return exceptionsBitmask & _EXCEPTION_FLT_DIVIDE_BY_ZERO; | |
case EXCEPTION_FLT_INEXACT_RESULT: | |
return exceptionsBitmask & _EXCEPTION_FLT_INEXACT_RESULT; | |
case EXCEPTION_FLT_INVALID_OPERATION: | |
return exceptionsBitmask & _EXCEPTION_FLT_INVALID_OPERATION; | |
case EXCEPTION_FLT_OVERFLOW: | |
return exceptionsBitmask & _EXCEPTION_FLT_OVERFLOW; | |
case EXCEPTION_FLT_STACK_CHECK: | |
return exceptionsBitmask & _EXCEPTION_FLT_STACK_CHECK; | |
case EXCEPTION_FLT_UNDERFLOW: | |
return exceptionsBitmask & _EXCEPTION_FLT_UNDERFLOW; | |
case EXCEPTION_ILLEGAL_INSTRUCTION: | |
return exceptionsBitmask & _EXCEPTION_ILLEGAL_INSTRUCTION; | |
case EXCEPTION_IN_PAGE_ERROR: | |
return exceptionsBitmask & _EXCEPTION_IN_PAGE_ERROR; | |
case EXCEPTION_INT_DIVIDE_BY_ZERO: | |
return exceptionsBitmask & _EXCEPTION_INT_DIVIDE_BY_ZERO; | |
case EXCEPTION_INT_OVERFLOW: | |
return exceptionsBitmask & _EXCEPTION_INT_OVERFLOW; | |
case EXCEPTION_INVALID_DISPOSITION: | |
return exceptionsBitmask & _EXCEPTION_INVALID_DISPOSITION; | |
case EXCEPTION_NONCONTINUABLE_EXCEPTION: | |
return exceptionsBitmask & _EXCEPTION_NONCONTINUABLE_EXCEPTION; | |
case EXCEPTION_PRIV_INSTRUCTION: | |
return exceptionsBitmask & _EXCEPTION_PRIV_INSTRUCTION; | |
case EXCEPTION_STACK_OVERFLOW: | |
return exceptionsBitmask & _EXCEPTION_STACK_OVERFLOW; | |
case EXCEPTION_GUARD_PAGE: | |
return exceptionsBitmask & _EXCEPTION_GUARD_PAGE; | |
case EXCEPTION_INVALID_HANDLE: | |
return exceptionsBitmask & _EXCEPTION_INVALID_HANDLE; | |
case DBG_CONTROL_C: | |
return exceptionsBitmask & _DBG_CONTROL_C; | |
case DBG_CONTROL_BREAK: | |
return exceptionsBitmask & _DBG_CONTROL_BREAK; | |
case STATUS_NO_MEMORY: | |
return exceptionsBitmask & _STATUS_NO_MEMORY; | |
case STATUS_DLL_INIT_FAILED: | |
return exceptionsBitmask & _STATUS_DLL_INIT_FAILED; | |
case STATUS_DLL_NOT_FOUND: | |
return exceptionsBitmask & _STATUS_DLL_NOT_FOUND; | |
case STATUS_ENTRYPOINT_NOT_FOUND: | |
return exceptionsBitmask & _STATUS_ENTRYPOINT_NOT_FOUND; | |
case MS_CPLUS_EXCEPTION: | |
return exceptionsBitmask & _MS_CPLUS_EXCEPTION; | |
default: | |
// it's not one of the typical exceptions and we have no setting for it. | |
// if it's first chance, don't report it. give it back for the process/os | |
// to handle. otherwise we'll report it to the debugger. | |
trace(LOG_ALWAYS, "\t -- first chance:%d, flags:%d", debugEvent.u.Exception.dwFirstChance, debugEvent.u.Exception.ExceptionRecord.ExceptionFlags); | |
if (debugEvent.u.Exception.dwFirstChance) | |
return false; | |
} | |
return true; | |
} |