Permalink
Fetching contributors…
Cannot retrieve contributors at this time
590 lines (484 sloc) 19.5 KB
/* @@@LICENSE
*
* Copyright (c) 2008-2013 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* LICENSE@@@ */
#include "Common.h"
#include "HostBase.h"
#include "ApplicationDescription.h"
//MDK-LAUNCHER #include "DockPositionManager.h"
#include "Localization.h"
#include "WebAppManager.h"
#include "Settings.h"
#include "Logging.h"
#include <sys/time.h>
#include <sys/resource.h>
#include <glib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <syslog.h>
#include <ucontext.h>
#include <fcntl.h>
#include <sys/file.h>
#include <QApplication>
#include <QtGui>
#include <QtGlobal>
/* Convenience macro for simulating crashes for debugging purposes only:
#define crash() { \
volatile int *ip = (volatile int *)0; \
*ip = 0; \
}
*/
//#define PRINT_MALLOC_STATS
#ifdef __cplusplus
extern "C" {
#endif
extern void malloc_stats(void);
#ifdef __cplusplus
};
#endif
static gchar* s_uiStr = NULL;
static gchar* s_appToLaunchStr = NULL;
static gchar* s_logLevelStr = NULL;
static gboolean s_useSysLog = false;
static gboolean s_colorLog = true;
static gboolean s_useTerminal = false;
static gchar* s_debugTrapStr = NULL;
static gboolean s_forceSoftwareRendering = false;
static gchar* s_mallocStatsFileStr = NULL;
static int s_mallocStatsInterval = -1;
// debugCrashes indicates whether the user has specified "-x on" in the command
// line args to enable debugging of crashes.
static bool debugCrashes = false;
// Used to allow us to bail from the handler when we're done debugging. This
// is meant to be set only from within a gdb seesion.
volatile bool stayInLoop = true;
int crashLogFD = -1;
pid_t sysmgrPid;
pid_t bootAnimPid;
int bootAnimPipeFd=-1, sysmgrPipeFd=-1;
int WebAppMgrPipeFd=-1;
char msgOkToContinue = 0xAB;
// Safe printf that does not call malloc (to avoid re-entrancy issue when
// called from the signal handler.
static void crash_printf(const char *format, ...) {
// Allocate the buffer staticly because we don't want to assume that
// there's a lot of stack space left at the time of the crash:
static char buf[512];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
ssize_t result = write(crashLogFD, buf, strlen(buf));
Q_UNUSED(result);
}
static void crash_flush() {
fsync(crashLogFD);
}
#if (defined(__i386__) || defined(__x86_64__))
// Register context dumper for ia32 / x64:
#if __WORDSIZE == 64
const char *const regNames[NGREG] = {
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
"RDI", "RSI", "RBP" "RBX", "RDX", "RAX", "RCX", "RIP",
"EFL", "CSGSFS", "ERR", "TRAPNO", "OLDMASK", "CR2"
};
#else // __WORDSIZE == 32
const char *const regNames[NGREG] = {
"GS", "FS", "ES", "DS", "EDI", "ESI", "EBP", "ESP",
"EBX", "EDX", "ECX", "EAX", "TRAPNO", "ERR", "EIP",
"CS", "EFL", "UESP", "SS"
};
#endif
void logCrashRegisterContext(int sig, siginfo_t *info, void *data) {
ucontext_t *context = reinterpret_cast<ucontext_t *>(data);
crash_printf("reg context {\n");
// Note: For ia32/x64, we dump all the registers because this is not for
// field deployment. Hence, there is no risk of violation of user privacy.
for (int i = 0; i < NGREG; i++) {
int value = context->uc_mcontext.gregs[i];
crash_printf(" %2d %8s = 0x%08x %d\n", i, regNames[i], value, value);
}
crash_printf("}\n");
crash_flush();
}
#elif defined(__arm__)
// Register context dumper for ARM:
void logCrashRegisterContext(int sig, siginfo_t *info, void *data) {
ucontext_t *context = reinterpret_cast<ucontext_t *>(data);
sigcontext *scon = &context->uc_mcontext;
crash_printf("reg context {\n");
// Dumping non-sensitive register content:
crash_printf(" // non-sensitive register content:\n");
crash_printf(" trap_no = 0x%08x %d\n", scon->trap_no, scon->trap_no);
crash_printf(" error_code = 0x%08x %d\n", scon->error_code, scon->error_code);
crash_printf(" oldmask = 0x%08x %d\n", scon->oldmask, scon->oldmask);
crash_printf(" arm_sp = 0x%08x %d\n", scon->arm_sp, scon->arm_sp);
crash_printf(" arm_lr = 0x%08x %d\n", scon->arm_lr, scon->arm_lr);
crash_printf(" arm_pc = 0x%08x %d\n", scon->arm_pc, scon->arm_pc);
crash_printf(" arm_cpsr = 0x%08x %d\n", scon->arm_cpsr, scon->arm_cpsr);
crash_printf(" fault_address = 0x%08x %d\n", scon->fault_address, scon->fault_address);
if (Settings::LunaSettings()->debug_doVerboseCrashLogging) {
// Dumping sensitive register content for internal debugging only.
// By default, this data should not be dumped in the field (to avoid
// potential violation of user privacy issues):
crash_printf(" arm_r0 = 0x%08x %d\n", scon->arm_r0, scon->arm_r0);
crash_printf(" arm_r1 = 0x%08x %d\n", scon->arm_r1, scon->arm_r1);
crash_printf(" arm_r2 = 0x%08x %d\n", scon->arm_r2, scon->arm_r2);
crash_printf(" arm_r3 = 0x%08x %d\n", scon->arm_r3, scon->arm_r3);
crash_printf(" arm_r4 = 0x%08x %d\n", scon->arm_r4, scon->arm_r4);
crash_printf(" arm_r5 = 0x%08x %d\n", scon->arm_r5, scon->arm_r5);
crash_printf(" arm_r6 = 0x%08x %d\n", scon->arm_r6, scon->arm_r6);
crash_printf(" arm_r7 = 0x%08x %d\n", scon->arm_r7, scon->arm_r7);
crash_printf(" arm_r8 = 0x%08x %d\n", scon->arm_r8, scon->arm_r8);
crash_printf(" arm_r9 = 0x%08x %d\n", scon->arm_r9, scon->arm_r9);
crash_printf(" arm_r10 = 0x%08x %d\n", scon->arm_r10, scon->arm_r10);
crash_printf(" arm_fp = 0x%08x %d\n", scon->arm_fp, scon->arm_fp);
crash_printf(" arm_ip = 0x%08x %d\n", scon->arm_ip, scon->arm_ip);
}
crash_printf("}\n");
crash_flush();
}
#endif // __arm__
static volatile bool hasCrashedInCrashHandler = false;
static void innerCrashHandler(int sig, siginfo_t *info, void *data);
static void outerCrashHandler(int sig, siginfo_t *info, void *data);
static void installInnerCrashHandler(int sig,
struct sigaction *previous_crash_action)
{
struct sigaction crash_action;
sigset_t block_mask;
sigfillset(&block_mask);
crash_action.sa_flags = SA_SIGINFO | SA_RESETHAND;
crash_action.sa_mask = block_mask;
crash_action.sa_sigaction = &innerCrashHandler;
// Install the handler for signals that we want to trap:
sigaction(sig, &crash_action, previous_crash_action);
}
static void installOuterCrashHandler(int sig)
{
struct sigaction crash_action;
sigset_t block_mask;
sigfillset(&block_mask);
sigdelset(&block_mask, SIGSEGV);
crash_action.sa_flags = SA_SIGINFO | SA_RESETHAND | SA_NODEFER;
crash_action.sa_mask = block_mask;
crash_action.sa_sigaction = &outerCrashHandler;
// Install the handler for signals that we want to trap:
sigaction(sig, &crash_action, NULL);
}
static gboolean mallocStatsCb(gpointer data)
{
char buf[30];
time_t cur_time;
time(&cur_time);
static pid_t my_pid = 0;
static char process_name[16] = { 0 };
if (!my_pid) {
my_pid = getpid();
}
if (!process_name[0]) {
::prctl(PR_GET_NAME, (unsigned long)process_name, 0, 0, 0);
process_name[sizeof(process_name) - 1] = '\0';
}
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
flock(STDERR_FILENO, LOCK_EX);
fflush(stderr);
fprintf(stderr, "\nMALLOC STATS FOR PROCESS: \"%s\" (PID: %d) AT [%ld.%ld] %s", process_name, my_pid, ts.tv_sec, ts.tv_nsec, ctime_r(&cur_time, buf));
fflush(stderr);
malloc_stats();
fprintf(stderr, "\n\n");
fflush(stderr);
fsync(STDERR_FILENO);
flock(STDERR_FILENO, LOCK_UN);
return TRUE;
}
static void initMallocStatsCb(GMainLoop* mainLoop, int secs)
{
// negative means no stats
if ((secs < 0) || (s_mallocStatsFileStr == NULL)) return;
GSource *timeoutSource = g_timeout_source_new_seconds(secs);
g_source_set_callback(timeoutSource, mallocStatsCb, NULL, NULL);
g_source_attach(timeoutSource, g_main_loop_get_context(mainLoop));
}
static void setupMallocStats(const char* mallocStatsFile)
{
FILE* file = ::freopen(mallocStatsFile, "a+", stderr);
if (!file) {
g_critical("Unable to open file: %s", mallocStatsFile);
}
}
static void innerCrashHandler(int sig, siginfo_t *info, void *data)
{
if (sig == SIGSEGV) {
// The inner crash handler is expecting to only see SIGSEGVs as a
// result of memory dumps that may have unknowingly touched a
// non-accessible page of memory. In this case, we simply bypass
// the instruction of the memory access and flag the error so that
// the outer crash handler know not to continue with this region
// of memory.
#if defined(__arm__)
ucontext_t *context = reinterpret_cast<ucontext_t *>(data);
sigcontext *scon = &context->uc_mcontext;
scon->arm_pc = scon->arm_pc + 4;
#endif
hasCrashedInCrashHandler = true;
// Note: We need to reinstall the inner signal handler because the
// outer handler may rely on it to safe access multiple regions of
// memory.
// Note: We only reinstall the inner signal handler if the fault is a
// known SIGSEGV fault that we know how to handle. Otherwise, let the
// normal faulting system handle the crash.
installInnerCrashHandler(sig, NULL);
}
}
static void outerCrashHandler(int sig, siginfo_t *info, void *data)
{
// Let webkit handle the crash if it wants to. If it returns true,
// the signal has been handled. Just return:
/*if (Palm::WebGlobal::handleSignal(sig, info, data)) {
// Webkit side has handled the signal. Hence, we're not going to crash,
// and will keep running. Therefore, we need to re-install the signal
// handler:
installOuterCrashHandler(sig);
return;
}*/
// Otherwise, report the crash diagnostics:
struct sigaction previous_crash_action;
installInnerCrashHandler(sig, &previous_crash_action);
const char *logFileName = Settings::LunaSettings()->logFileName.c_str();
char logFileNameBuffer[64];
if (Settings::LunaSettings()->debug_doVerboseCrashLogging) {
snprintf(logFileNameBuffer, 64,
"/media/internal/lunasysmgr.%d.verbose.log", sysmgrPid);
logFileName = logFileNameBuffer;
}
crashLogFD = open(logFileName, O_WRONLY | O_CREAT | O_TRUNC);
#if 0 // Disable until we can do this without calling malloc and free:
int memTotal, memFree, swapTotal, swapFree, memchuteFree, memUsage;
#endif
if (crashLogFD != -1) {
crash_printf("LunaSysMgr.%d: Caught signal %d\n", sysmgrPid, sig);
crash_flush();
// Dump the register context for the crash to the logs:
logCrashRegisterContext(sig, info, data);
// Let webkit do some analysis on the crash information and log report
// as appropriate:
#ifndef FIX_FOR_QT
Palm::WebGlobal::reportCrashDiagnostics(sig, info, data, crashLogFD,
Settings::LunaSettings()->debug_doVerboseCrashLogging,
&hasCrashedInCrashHandler);
#endif
crash_flush();
crash_printf("LunaSysMgr.%d: Caught signal %d END report\n",
sysmgrPid, sig);
crash_flush();
}
// If the option to debug crashes is enabled, then we'll enter an infinite
// loop here to capture the crash conditions until a gdb session can be
// attached.
//
// By default, this option is disabled, and the crashHandler will return
// to the crashing instruction. Since the crashHandler is set up to
// fire only once (see SA_RESETHAND option which resets the handler after
// it has fired), when we return to the crashing pc, we'll fault again.
// This time, the fault will be handled by the default system handler and
// generate a core or minicore as appropriate.
if (debugCrashes || Settings::LunaSettings()->debug_loopInCrashHandler) {
// Infinite loop until we turn off stayInLoop using gdb:
while (stayInLoop) {
// If we get here, sit and wait for someone to come and debug
// this device.
}
}
// Restore the previous handler before we return. Otherwise, the inner
// handler will perpetually skip over faulting addresses:
sigaction(sig, &previous_crash_action, NULL);
// Close the file:
fsync(crashLogFD);
close(crashLogFD);
crashLogFD = -1;
}
void qtMsgHandler(QtMsgType type, const char *str) {
switch(type)
{
case QtDebugMsg:
g_debug("QDebug: %s", str);
break;
case QtWarningMsg:
g_warning("QWarning: %s", str);
break;
case QtCriticalMsg:
g_critical("QCritical: %s", str);
break;
case QtFatalMsg:
g_error("QFatal: %s", str);
break;
default:
g_message("QMessage: %s", str);
break;
}
}
static void parseCommandlineOptions(int argc, char** argv)
{
GError* error = NULL;
GOptionContext* context = NULL;
static GOptionEntry entries[] = {
{ "ui", 'u', 0, G_OPTION_ARG_STRING, &s_uiStr, "UI type to launch (minimal, luna)", "name" },
{ "app", 'a', 0, G_OPTION_ARG_STRING, &s_appToLaunchStr, "App Id of app to launch", "id" },
{ "logger", 'l', 0, G_OPTION_ARG_STRING, &s_logLevelStr, "log level", "level"},
{ "syslog", 's', 0, G_OPTION_ARG_NONE, &s_useSysLog, "Use syslog", NULL },
{ "colorlogging", 'c', 0, G_OPTION_ARG_NONE, &s_colorLog, "Color logging on or off", NULL }, // use -c=0 or --colorlogging=0 to disable color logging
{ "terminal", 't', 0, G_OPTION_ARG_NONE, &s_useTerminal, "Use terminal for logs", NULL },
{ "debug-trap", 'x', 0, G_OPTION_ARG_STRING, &s_debugTrapStr, "debug trap (on/ off)", "state"},
{ "force-software-rendering", 'S', 0, G_OPTION_ARG_NONE, &s_forceSoftwareRendering, "Force Software rendering", NULL},
{ "malloc-stats-file", 'm', 0, G_OPTION_ARG_STRING, &s_mallocStatsFileStr, "File for logging malloc stats", "file" },
{ "malloc-stats-interval", 'i', 0, G_OPTION_ARG_INT, &s_mallocStatsInterval, "Interval at which to log malloc stats", "seconds" },
{ NULL }
};
context = g_option_context_new(NULL);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_parse (context, &argc, &argv, &error);
#if !defined(HAVE_OPENGL)
// if there's no OpenGL, then implicitely force software rendering
s_forceSoftwareRendering = true;
#endif
if (s_uiStr && strcasecmp(s_uiStr, "minimal") == 0)
Settings::LunaSettings()->uiType = Settings::UI_MINIMAL;
else
Settings::LunaSettings()->uiType = Settings::UI_LUNA;
Settings::LunaSettings()->logger_useSyslog = s_useSysLog;
Settings::LunaSettings()->logger_useColor = s_colorLog;
Settings::LunaSettings()->logger_useTerminal = s_useTerminal;
#if SHIPPING_VERSION
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_CRITICAL;
#else
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_DEBUG;
#endif
Settings::LunaSettings()->forceSoftwareRendering = s_forceSoftwareRendering;
if (s_forceSoftwareRendering)
Settings::LunaSettings()->atlasEnabled = false;
if (s_logLevelStr)
{
if (0 == strcasecmp(s_logLevelStr, "error"))
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_ERROR;
else if (0 == strcasecmp(s_logLevelStr, "critical"))
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_CRITICAL;
else if (0 == strcasecmp(s_logLevelStr, "warning"))
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_WARNING;
else if (0 == strcasecmp(s_logLevelStr, "message"))
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_MESSAGE;
else if (0 == strcasecmp(s_logLevelStr, "info"))
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_INFO;
else if (0 == strcasecmp(s_logLevelStr, "debug"))
Settings::LunaSettings()->logger_level = G_LOG_LEVEL_DEBUG;
}
g_option_context_free(context);
}
static void generateGoodBacktraceTerminateHandler()
{
volatile int* p = 0;
*p = 0;
exit(-1);
}
int appArgc = 0;
char** appArgv = 0;
int main( int argc, char** argv)
{
appArgc = argc;
appArgv = argv;
std::set_terminate(generateGoodBacktraceTerminateHandler);
g_thread_init(NULL);
const char *renderMode;
#if defined(TARGET_DEVICE) && defined(HAVE_OPENGL)
::setenv("QT_PLUGIN_PATH", "/usr/plugins", 1);
renderMode = "HW egl";
#elif defined(TARGET_DEVICE) || defined(TARGET_EMULATOR)
::setenv("QT_PLUGIN_PATH", "/usr/plugins", 1);
renderMode = "Software";
#elif defined(HAVE_OPENGL)
renderMode = "HW OpenGL";
#else
renderMode = "Software";
#endif
g_debug("SysMgr compiled against Qt %s, running on %s, %s render mode requested", QT_VERSION_STR, qVersion(), renderMode);
// Command-Line options
parseCommandlineOptions(argc, argv);
if (s_debugTrapStr && 0 == strcasecmp(s_debugTrapStr, "on")) {
debugCrashes = true;
}
if (s_mallocStatsFileStr) {
setupMallocStats(s_mallocStatsFileStr);
}
sysmgrPid = getpid();
// Load Settings (first!)
Settings* settings = Settings::LunaSettings();
// Initialize logging handler
g_log_set_default_handler(logFilter, NULL);
#if defined(TARGET_DESKTOP)
// use terminal logging when running on desktop
settings->logger_useTerminal = true;
#endif
// disable color logging using an environment variable. Useful when run from QtCreator
const char* useColor = ::getenv("COLOR_LOGGING");
if (useColor)
settings->logger_useColor = (useColor[0] != 0 && useColor[0] != '0');
HostBase* host = HostBase::instance();
// the resolution is just a hint, the actual
// resolution may get picked up from the fb driver on arm
host->init(settings->displayWidth, settings->displayHeight);
#if defined(TARGET_DEVICE) && defined(HAVE_OPENGL)
if (settings->forceSoftwareRendering)
::setenv("QT_QPA_PLATFORM", "palm-soft", 0);
else
::setenv("QT_QPA_PLATFORM", "palm", 0);
#else
// Do not override the value if the variable exists
::setenv("QT_QPA_PLATFORM", "palm", 0);
#endif
#if defined(TARGET_DEVICE) && defined(HAVE_OPENGL)
if (!settings->forceSoftwareRendering)
::setenv("QWS_DISPLAY", "egl", 1);
#endif
// Install the handler for signals that we want to trap:
// Note: We install the handlers after we initialize the setting because
// we may do something different depending on the settings values.
installOuterCrashHandler(SIGILL);
installOuterCrashHandler(SIGSEGV);
installOuterCrashHandler(SIGTERM);
// Not needed anymore?
::prctl(PR_SET_NAME, (unsigned long) "WebAppMgr", 0, 0, 0);
::prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
const HostInfo* info = &(HostBase::instance()->getInfo());
WebAppManager::instance()->setHostInfo(info);
initMallocStatsCb(WebAppManager::instance()->mainLoop(), s_mallocStatsInterval);
logInit();
// Start the Browser App Launcher
#ifdef NO_WEBKIT_INIT
//WindowServer::instance()->bootupFinished();
#else
WebAppManager::instance()->run(); // Sync execution of the task
#endif
return 0;
}