diff options
Diffstat (limited to 'simulator/app/LogWindow.cpp')
| -rw-r--r-- | simulator/app/LogWindow.cpp | 1156 |
1 files changed, 0 insertions, 1156 deletions
diff --git a/simulator/app/LogWindow.cpp b/simulator/app/LogWindow.cpp deleted file mode 100644 index b19debbea..000000000 --- a/simulator/app/LogWindow.cpp +++ /dev/null @@ -1,1156 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -// Display runtime log output. -// - -// For compilers that support precompilation, include "wx/wx.h". -#include "wx/wxprec.h" - -// Otherwise, include all standard headers -#ifndef WX_PRECOMP -# include "wx/wx.h" -#endif -#include "wx/image.h" // needed for Windows build -#include "wx/dcbuffer.h" - -#include "LogWindow.h" -#include "LogMessage.h" -#include "LogPrefsDialog.h" -#include "MyApp.h" -#include "Preferences.h" -#include "Resource.h" -#include "UserEventMessage.h" - -#include <errno.h> - -static int android_snprintfBuffer(char** pBuf, int bufLen, const char* format, ...); -static int android_vsnprintfBuffer(char** pBuf, int bufLen, const char* format, va_list args); - - -using namespace android; - -#if 0 // experiment -- works on Win32, but not with GTK -class MyTextCtrl : public wxTextCtrl { -public: - MyTextCtrl(wxWindow* parent, wxWindowID id, const wxString& value, - const wxPoint& pos, const wxSize& size, int style = 0) - : wxTextCtrl(parent, id, value, pos, size, style) - { - printf("***************** MyTextCtrl!\n"); - } - - void OnScroll(wxScrollWinEvent& event); - void OnScrollBottom(wxScrollWinEvent& event); - -private: - DECLARE_EVENT_TABLE() -}; - -BEGIN_EVENT_TABLE(MyTextCtrl, wxTextCtrl) - EVT_SCROLLWIN(MyTextCtrl::OnScroll) - EVT_SCROLLWIN_BOTTOM(MyTextCtrl::OnScrollBottom) -END_EVENT_TABLE() - -void MyTextCtrl::OnScroll(wxScrollWinEvent& event) -{ - printf("OnScroll!\n"); -} - -void MyTextCtrl::OnScrollBottom(wxScrollWinEvent& event) -{ - printf("OnScrollBottom!\n"); -} -#endif - - -BEGIN_EVENT_TABLE(LogWindow, wxDialog) - EVT_CLOSE(LogWindow::OnClose) - EVT_MOVE(LogWindow::OnMove) - EVT_COMBOBOX(IDC_LOG_LEVEL, LogWindow::OnLogLevel) - EVT_BUTTON(IDC_LOG_CLEAR, LogWindow::OnLogClear) - EVT_BUTTON(IDC_LOG_PAUSE, LogWindow::OnLogPause) - EVT_BUTTON(IDC_LOG_PREFS, LogWindow::OnLogPrefs) -END_EVENT_TABLE() - -/* - * Information about log levels. - * - * Each entry here corresponds to an entry in the combo box. The first - * letter of each name should be unique. - */ -static const struct { - wxString name; - android_LogPriority priority; -} gLogLevels[] = { - { wxT("Verbose"), ANDROID_LOG_VERBOSE }, - { wxT("Debug"), ANDROID_LOG_DEBUG }, - { wxT("Info"), ANDROID_LOG_INFO }, - { wxT("Warn"), ANDROID_LOG_WARN }, - { wxT("Error"), ANDROID_LOG_ERROR } -}; - - -/* - * Create a new LogWindow. This should be a child of the main frame. - */ -LogWindow::LogWindow(wxWindow* parent) - : wxDialog(parent, wxID_ANY, wxT("Log Output"), wxDefaultPosition, - wxDefaultSize, - wxCAPTION | wxSYSTEM_MENU | wxCLOSE_BOX | wxRESIZE_BORDER), - mDisplayArray(NULL), mMaxDisplayMsgs(0), mPaused(false), - mMinPriority(ANDROID_LOG_VERBOSE), - mHeaderFormat(LogPrefsDialog::kHFFull), - mSingleLine(false), mExtraSpacing(0), mPointSize(10), mUseColor(true), - mFontMonospace(true), mWriteFile(false), mTruncateOld(true), mLogFp(NULL), - mNewlyShown(false), mLastPosition(wxDefaultPosition), mVisible(false) -{ - ConstructControls(); - - Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); - - int poolSize = 10240; // 10MB - pPrefs->GetInt("log-pool-size-kbytes", &poolSize); - assert(poolSize > 0); - mPool.Resize(poolSize * 1024); - - mMaxDisplayMsgs = 1000; - pPrefs->GetInt("log-display-msg-count", &mMaxDisplayMsgs); - assert(mMaxDisplayMsgs > 0); - mDisplayArray = new LogMessage*[mMaxDisplayMsgs]; - memset(mDisplayArray, 0, sizeof(LogMessage*) * mMaxDisplayMsgs); - mTopPtr = -1; - mNextPtr = 0; - - int tmpInt = (int) mHeaderFormat; - pPrefs->GetInt("log-header-format", &tmpInt); - mHeaderFormat = (LogPrefsDialog::HeaderFormat) tmpInt; - pPrefs->GetBool("log-single-line", &mSingleLine); - pPrefs->GetInt("log-extra-spacing", &mExtraSpacing); - pPrefs->GetInt("log-point-size", &mPointSize); - pPrefs->GetBool("log-use-color", &mUseColor); - pPrefs->SetBool("log-font-monospace", &mFontMonospace); - SetTextStyle(); - - mFileName = wxT("/tmp/android-log.txt"); - pPrefs->GetBool("log-write-file", &mWriteFile); - pPrefs->GetString("log-filename", /*ref*/mFileName); - pPrefs->GetBool("log-truncate-old", &mTruncateOld); - - PrepareLogFile(); -} - -/* - * Destroy everything we own. - */ -LogWindow::~LogWindow(void) -{ - ClearDisplay(); - delete[] mDisplayArray; - - if (mLogFp != NULL) - fclose(mLogFp); -} - -/* - * Set the text style, based on our preferences. - */ -void LogWindow::SetTextStyle(void) -{ - wxTextCtrl* pTextCtrl; - pTextCtrl = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT); - wxTextAttr style; - style = pTextCtrl->GetDefaultStyle(); - - if (mFontMonospace) { - wxFont font(mPointSize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, - wxFONTWEIGHT_NORMAL); - style.SetFont(font); - } else { - wxFont font(mPointSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, - wxFONTWEIGHT_NORMAL); - style.SetFont(font); - } - - pTextCtrl->SetDefaultStyle(style); -} - -/* - * Set up the goodies in the window. - * - * Also initializes mMinPriority. - */ -void LogWindow::ConstructControls(void) -{ - Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); - wxPanel* base = new wxPanel(this, wxID_ANY); - wxBoxSizer* masterSizer = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* indentSizer = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* configPrioritySizer = new wxBoxSizer(wxHORIZONTAL); - wxGridSizer* configSizer = new wxGridSizer(4, 1); - - /* - * Configure log level combo box. - */ - wxComboBox* logLevel; - int defaultLogLevel = 1; - pPrefs->GetInt("log-display-level", &defaultLogLevel); - logLevel = new wxComboBox(base, IDC_LOG_LEVEL, wxT(""), - wxDefaultPosition, wxDefaultSize, 0, NULL, - wxCB_READONLY /*| wxSUNKEN_BORDER*/); - for (int i = 0; i < NELEM(gLogLevels); i++) { - logLevel->Append(gLogLevels[i].name); - logLevel->SetClientData(i, (void*) gLogLevels[i].priority); - } - logLevel->SetSelection(defaultLogLevel); - mMinPriority = gLogLevels[defaultLogLevel].priority; - - /* - * Set up stuff at the bottom, starting with the options - * at the bottom left. - */ - configPrioritySizer->Add(new wxStaticText(base, wxID_ANY, wxT("Log level:"), - wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), - 0, wxALIGN_CENTER_VERTICAL); - configPrioritySizer->AddSpacer(kInterSpacing); - configPrioritySizer->Add(logLevel); - - wxButton* clear = new wxButton(base, IDC_LOG_CLEAR, wxT("&Clear"), - wxDefaultPosition, wxDefaultSize, 0); - wxButton* pause = new wxButton(base, IDC_LOG_PAUSE, wxT("&Pause"), - wxDefaultPosition, wxDefaultSize, 0); - wxButton* prefs = new wxButton(base, IDC_LOG_PREFS, wxT("C&onfigure"), - wxDefaultPosition, wxDefaultSize, 0); - - configSizer->Add(configPrioritySizer, 0, wxALIGN_LEFT); - configSizer->Add(clear, 0, wxALIGN_CENTER); - configSizer->Add(pause, 0, wxALIGN_CENTER); - configSizer->Add(prefs, 0, wxALIGN_RIGHT); - - /* - * Create text ctrl. - */ - wxTextCtrl* pTextCtrl; - pTextCtrl = new wxTextCtrl(base, IDC_LOG_TEXT, wxT(""), - wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2 | wxTE_NOHIDESEL | - wxHSCROLL); - - /* - * Add components to master sizer. - */ - masterSizer->AddSpacer(kEdgeSpacing); - masterSizer->Add(pTextCtrl, 1, wxEXPAND); - masterSizer->AddSpacer(kInterSpacing); - masterSizer->Add(configSizer, 0, wxEXPAND); - masterSizer->AddSpacer(kEdgeSpacing); - - /* - * Indent from sides. - */ - indentSizer->AddSpacer(kEdgeSpacing); - indentSizer->Add(masterSizer, 1, wxEXPAND); - indentSizer->AddSpacer(kEdgeSpacing); - - base->SetSizer(indentSizer); - - indentSizer->Fit(this); // shrink-to-fit - indentSizer->SetSizeHints(this); // define minimum size -} - -/* - * In some cases, this means the user has clicked on our "close" button. - * We don't really even want one, but both WinXP and KDE put one on our - * window whether we want it or not. So, we make it work as a "hide" - * button instead. - * - * This also gets called when the app is shutting down, and we do want - * to destroy ourselves then, saving various information about our state. - */ -void LogWindow::OnClose(wxCloseEvent& event) -{ - /* just hide the window, unless we're shutting down */ - if (event.CanVeto()) { - event.Veto(); - Show(false); - return; - } - - /* - * Save some preferences. - */ - SaveWindowPrefs(); - - /* if we can't veto the Close(), destroy ourselves */ - Destroy(); -} - -/* - * Save all of our preferences to the config file. - */ -void LogWindow::SaveWindowPrefs(void) -{ - Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); - - /* - * Save shown/hidden state. - */ - pPrefs->SetBool("window-log-show", IsShown()); - - /* - * Limits and formatting prefs. - */ - pPrefs->SetInt("log-display-msg-count", mMaxDisplayMsgs); - pPrefs->SetInt("log-pool-size-kbytes", mPool.GetMaxSize() / 1024); - - pPrefs->SetInt("log-header-format", mHeaderFormat); - pPrefs->SetBool("log-single-line", mSingleLine); - pPrefs->SetInt("log-extra-spacing", mExtraSpacing); - pPrefs->SetInt("log-point-size", mPointSize); - pPrefs->SetBool("log-use-color", mUseColor); - pPrefs->SetBool("log-font-monospace", mFontMonospace); - - pPrefs->SetBool("log-write-file", mWriteFile); - pPrefs->SetString("log-filename", mFileName.ToAscii()); - pPrefs->SetBool("log-truncate-old", mTruncateOld); - - /* - * Save window size and position. - */ - wxPoint posn; - wxSize size; - - assert(pPrefs != NULL); - - posn = GetPosition(); - size = GetSize(); - - pPrefs->SetInt("window-log-x", posn.x); - pPrefs->SetInt("window-log-y", posn.y); - pPrefs->SetInt("window-log-width", size.GetWidth()); - pPrefs->SetInt("window-log-height", size.GetHeight()); - - /* - * Save current setting of debug level combo box. - */ - wxComboBox* pCombo; - int selection; - pCombo = (wxComboBox*) FindWindow(IDC_LOG_LEVEL); - selection = pCombo->GetSelection(); - pPrefs->SetInt("log-display-level", selection); -} - -/* - * Return the desired position and size. - */ -/*static*/ wxRect LogWindow::GetPrefWindowRect(void) -{ - Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); - int x, y, width, height; - - assert(pPrefs != NULL); - - x = y = 10; - width = 500; - height = 200; - - /* these don't modify the arg if the pref doesn't exist */ - pPrefs->GetInt("window-log-x", &x); - pPrefs->GetInt("window-log-y", &y); - pPrefs->GetInt("window-log-width", &width); - pPrefs->GetInt("window-log-height", &height); - - return wxRect(x, y, width, height); -} - -/* - * Under Linux+GTK, the first time you show the window, it appears where - * it's supposed to. If you then hide it and show it again, it gets - * moved on top of the parent window. After that, you can reposition it - * and it remembers its position across hide/show. - * - * To counter this annoyance, we save the position when we hide, and - * reset the position after a show. The "newly shown" flag ensures that - * we only reposition the window as the result of a Show(true) call. - * - * Sometimes, something helpful will shift the window over if it's - * partially straddling a seam between two monitors. I don't see an easy - * way to block this, and I'm not sure I want to anyway. - */ -void LogWindow::OnMove(wxMoveEvent& event) -{ - wxPoint point; - point = event.GetPosition(); - //printf("Sim: log window is at (%d,%d) (new=%d)\n", point.x, point.y, - // mNewlyShown); - - if (mNewlyShown) { - if (mLastPosition == wxDefaultPosition) { - //printf("Sim: no last position established\n"); - } else { - Move(mLastPosition); - } - - mNewlyShown = false; - } -} - -/* - * Set the "newly shown" flag. - */ -bool LogWindow::Show(bool show) -{ - if (show) { - mNewlyShown = true; - Redisplay(); - } else { - mLastPosition = GetPosition(); - } - - mVisible = show; - return wxDialog::Show(show); -} - -/* - * User has adjusted the log level. Update the display appropriately. - * - * This is a wxEVT_COMMAND_COMBOBOX_SELECTED event. - */ -void LogWindow::OnLogLevel(wxCommandEvent& event) -{ - int selection; - android_LogPriority priority; - - selection = event.GetInt(); - wxComboBox* pCombo = (wxComboBox*) FindWindow(IDC_LOG_LEVEL); - priority = (android_LogPriority) (long)pCombo->GetClientData(event.GetInt()); - - printf("Sim: log level selected: %d (%s)\n", (int) priority, - (const char*) gLogLevels[selection].name.ToAscii()); - mMinPriority = priority; - Redisplay(); -} - -/* - * Clear out the log. - */ -void LogWindow::OnLogClear(wxCommandEvent& event) -{ - ClearDisplay(); - mPool.Clear(); -} - -/* - * Handle the pause/resume button. - * - * If we're un-pausing, we need to get caught up. - */ -void LogWindow::OnLogPause(wxCommandEvent& event) -{ - mPaused = !mPaused; - - wxButton* pButton = (wxButton*) FindWindow(IDC_LOG_PAUSE); - if (mPaused) { - pButton->SetLabel(wxT("&Resume")); - - mPool.SetBookmark(); - } else { - pButton->SetLabel(wxT("&Pause")); - - LogMessage* pMsg = mPool.GetBookmark(); - if (pMsg == NULL) { - /* bookmarked item fell out of pool */ - printf("--- bookmark was lost, redisplaying\n"); - Redisplay(); - } else { - /* - * The bookmark points to the last item added to the display. - * We want to chase its "prev" pointer to walk toward the head - * of the list, adding items from oldest to newest. - */ - pMsg = pMsg->GetPrev(); - while (pMsg != NULL) { - if (FilterMatches(pMsg)) - AddToDisplay(pMsg); - pMsg = pMsg->GetPrev(); - } - } - } -} - -/* - * Open log preferences dialog. - */ -void LogWindow::OnLogPrefs(wxCommandEvent& event) -{ - LogPrefsDialog dialog(this); - - /* - * Set up the dialog. - */ - dialog.mHeaderFormat = mHeaderFormat; - dialog.mSingleLine = mSingleLine; - dialog.mExtraSpacing = mExtraSpacing; - dialog.mPointSize = mPointSize; - dialog.mUseColor = mUseColor; - dialog.mFontMonospace = mFontMonospace; - - dialog.mDisplayMax = mMaxDisplayMsgs; - dialog.mPoolSizeKB = mPool.GetMaxSize() / 1024; - - dialog.mWriteFile = mWriteFile; - dialog.mFileName = mFileName; - dialog.mTruncateOld = mTruncateOld; - - /* - * Show it. If they hit "OK", copy the updated values out, and - * re-display the log output. - */ - if (dialog.ShowModal() == wxID_OK) { - /* discard old display arra */ - ClearDisplay(); - delete[] mDisplayArray; - - mHeaderFormat = dialog.mHeaderFormat; - mSingleLine = dialog.mSingleLine; - mExtraSpacing = dialog.mExtraSpacing; - mPointSize = dialog.mPointSize; - mUseColor = dialog.mUseColor; - mFontMonospace = dialog.mFontMonospace; - - assert(dialog.mDisplayMax > 0); - assert(dialog.mPoolSizeKB > 0); - mMaxDisplayMsgs = dialog.mDisplayMax; - mPool.Resize(dialog.mPoolSizeKB * 1024); - - mWriteFile = dialog.mWriteFile; - if (mLogFp != NULL && mFileName != dialog.mFileName) { - printf("--- log file name changed, closing\n"); - fclose(mLogFp); - mLogFp = NULL; - } - mFileName = dialog.mFileName; - mTruncateOld = dialog.mTruncateOld; - - mDisplayArray = new LogMessage*[mMaxDisplayMsgs]; - memset(mDisplayArray, 0, sizeof(LogMessage*) * mMaxDisplayMsgs); - Redisplay(); - - PrepareLogFile(); - } -} - -/* - * Handle a log message "user event". This should only be called in - * the main UI thread. - * - * We take ownership of "*pLogMessage". - */ -void LogWindow::AddLogMessage(LogMessage* pLogMessage) -{ - mPool.Add(pLogMessage); - - if (!mPaused && mVisible && FilterMatches(pLogMessage)) { - /* - * Thought: keep a reference to the previous message. If it - * matches in most fields (all except timestamp?), hold it and - * increment a counter. If we get a message that doesn't match, - * or a timer elapses, synthesize a "previous message repeated N - * times" string. - */ - AddToDisplay(pLogMessage); - } - - // release the initial ref caused by allocation - pLogMessage->Release(); - - if (mLogFp != NULL) - LogToFile(pLogMessage); -} - -/* - * Clear out the display, releasing any log messages held in the display - * array. - */ -void LogWindow::ClearDisplay(void) -{ - wxTextCtrl* pTextCtrl; - pTextCtrl = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT); - pTextCtrl->Clear(); - - /* - * Just run through the entire array. - */ - for (int i = 0; i < mMaxDisplayMsgs; i++) { - if (mDisplayArray[i] != NULL) { - mDisplayArray[i]->Release(); - mDisplayArray[i] = NULL; - } - } - mTopPtr = -1; - mNextPtr = 0; -} - -/* - * Clear the current display and regenerate it from the log pool. We need - * to do this whenever we change filters or log message formatting. - */ -void LogWindow::Redisplay(void) -{ - /* - * Freeze output rendering so it doesn't flash during update. Doesn't - * seem to help for GTK, and it leaves garbage on the screen in WinXP, - * so I'm leaving it commented out. - */ - //wxTextCtrl* pText = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT); - //pText->Freeze(); - - //printf("--- redisplay\n"); - ClearDisplay(); - - /* - * Set up the default wxWidgets text style stuff. - */ - SetTextStyle(); - - /* - * Here's the plan: - * - Start at the head of the pool (where the most recently added - * items are). - * - Check to see if the current item passes our filter. If it does, - * increment the "found count". - * - Continue in this manner until we run out of pool or have - * sufficient items to fill the screen. - * - Starting from the current position, walk back toward the head, - * adding the items that meet the current filter criteria. - * - * Don't forget that the log pool could be empty. - */ - LogMessage* pMsg = mPool.GetHead(); - - if (pMsg != NULL) { - int foundCount = 0; - - // note this stops before it runs off the end - while (pMsg->GetNext() != NULL && foundCount < mMaxDisplayMsgs) { - if (FilterMatches(pMsg)) - foundCount++; - pMsg = pMsg->GetNext(); - } - - while (pMsg != NULL) { - if (FilterMatches(pMsg)) - AddToDisplay(pMsg); - pMsg = pMsg->GetPrev(); - } - } - - //pText->Thaw(); -} - - -/* - * Returns "true" if the currently specified filters would allow this - * message to be shown. - */ -bool LogWindow::FilterMatches(const LogMessage* pLogMessage) -{ - if (pLogMessage->GetPriority() >= mMinPriority) - return true; - else - return false; -} - -/* - * Realloc the array of pointers, and remove anything from the display - * that should no longer be there. - */ -void LogWindow::SetMaxDisplayMsgs(int max) -{ - Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); - - pPrefs->SetInt("log-display-msg-count", max); -} - -/* - * Add the message to the display array and to the screen. - */ -void LogWindow::AddToDisplay(LogMessage* pLogMessage) -{ - wxTextCtrl* pTextCtrl; - pTextCtrl = (wxTextCtrl*) FindWindow(IDC_LOG_TEXT); - - if (mNextPtr == mTopPtr) { - /* - * The display array is full. - * - * We need to eliminate the topmost entry. This requires removing - * it from the array and removing the text from the wxTextCtrl. - */ - pTextCtrl->Remove(0, mDisplayArray[mTopPtr]->GetTextCtrlLen()); - mDisplayArray[mTopPtr]->Release(); - mTopPtr = (mTopPtr + 1) % mMaxDisplayMsgs; - } - - /* - * Add formatted text to the text ctrl. Track how much actual space - * is required. The space may be different on Win32 (CRLF-based) vs. - * GTK (LF-based), so we need to measure it, not compute it from the - * text string. - */ - long lastBefore, lastAfter; - //long insertBefore; - //insertBefore = pTextCtrl->GetInsertionPoint(); - lastBefore = pTextCtrl->GetLastPosition(); - FormatMessage(pLogMessage, pTextCtrl); - lastAfter = pTextCtrl->GetLastPosition(); - pLogMessage->SetTextCtrlLen(lastAfter - lastBefore); - - /* - * If we restore the old insertion point, we will be glued to where - * we were. This is okay until we start deleting text from the top, - * at which point we need to adjust it to retain our position. - * - * If we set the insertion point to the bottom, we effectively - * implement "scroll to bottom on output". - * - * If we don't set it at all, we get slightly strange behavior out - * of GTK, which seems to be par for the course here. - */ - //pTextCtrl->SetInsertionPoint(insertBefore); // restore insertion pt - pTextCtrl->SetInsertionPoint(lastAfter); - - /* add it to array, claim ownership */ - mDisplayArray[mNextPtr] = pLogMessage; - pLogMessage->Acquire(); - - /* adjust pointers */ - if (mTopPtr < 0) // first time only - mTopPtr = 0; - mNextPtr = (mNextPtr + 1) % mMaxDisplayMsgs; -} - - -/* - * Return a human-readable string for the priority level. Always returns - * a valid string. - */ -static const wxCharBuffer GetPriorityString(android_LogPriority priority) -{ - int idx; - - idx = (int) priority - (int) ANDROID_LOG_VERBOSE; - if (idx < 0 || idx >= NELEM(gLogLevels)) - return "?unknown?"; - return gLogLevels[idx].name.ToAscii(); -} - -/* - * Format a message and write it to the text control. - */ -void LogWindow::FormatMessage(const LogMessage* pLogMessage, - wxTextCtrl* pTextCtrl) -{ -#if defined(HAVE_LOCALTIME_R) - struct tm tmBuf; -#endif - struct tm* ptm; - char timeBuf[32]; - char msgBuf[256]; - int msgLen = 0; - char* outBuf; - char priChar; - LogPrefsDialog::HeaderFormat headerFmt; - - headerFmt = mHeaderFormat; - if (pLogMessage->GetInternal()) - headerFmt = LogPrefsDialog::kHFInternal; - - priChar = ((const char*)GetPriorityString(pLogMessage->GetPriority()))[0]; - - /* - * Get the current date/time in pretty form - * - * It's often useful when examining a log with "less" to jump to - * a specific point in the file by searching for the date/time stamp. - * For this reason it's very annoying to have regexp meta characters - * in the time stamp. Don't use forward slashes, parenthesis, - * brackets, asterisks, or other special chars here. - */ - time_t when = pLogMessage->GetWhen(); - const char* fmt = NULL; -#if defined(HAVE_LOCALTIME_R) - ptm = localtime_r(&when, &tmBuf); -#else - ptm = localtime(&when); -#endif - switch (headerFmt) { - case LogPrefsDialog::kHFFull: - case LogPrefsDialog::kHFInternal: - fmt = "%m-%d %H:%M:%S"; - break; - case LogPrefsDialog::kHFBrief: - case LogPrefsDialog::kHFMinimal: - fmt = "%H:%M:%S"; - break; - default: - break; - } - if (fmt != NULL) - strftime(timeBuf, sizeof(timeBuf), fmt, ptm); - else - strcpy(timeBuf, "-"); - - const int kMaxExtraNewlines = 2; - char hdrNewline[2]; - char finalNewlines[kMaxExtraNewlines+1 +1]; - - if (mSingleLine) - hdrNewline[0] = ' '; - else - hdrNewline[0] = '\n'; - hdrNewline[1] = '\0'; - - assert(mExtraSpacing <= kMaxExtraNewlines); - int i; - for (i = 0; i < mExtraSpacing+1; i++) - finalNewlines[i] = '\n'; - finalNewlines[i] = '\0'; - - wxTextAttr msgColor; - switch (pLogMessage->GetPriority()) { - case ANDROID_LOG_WARN: - msgColor.SetTextColour(*wxBLUE); - break; - case ANDROID_LOG_ERROR: - msgColor.SetTextColour(*wxRED); - break; - case ANDROID_LOG_VERBOSE: - case ANDROID_LOG_DEBUG: - case ANDROID_LOG_INFO: - default: - msgColor.SetTextColour(*wxBLACK); - break; - } - if (pLogMessage->GetInternal()) - msgColor.SetTextColour(*wxGREEN); - - /* - * Construct a buffer containing the log header. - */ - bool splitHeader = true; - outBuf = msgBuf; - switch (headerFmt) { - case LogPrefsDialog::kHFFull: - splitHeader = true; - msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf), - "[ %s %5d %c/%-6.6s]%s", - timeBuf, pLogMessage->GetPid(), priChar, - pLogMessage->GetTag(), hdrNewline); - break; - case LogPrefsDialog::kHFBrief: - splitHeader = true; - msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf), - "[%s %5d]%s", - timeBuf, pLogMessage->GetPid(), hdrNewline); - break; - case LogPrefsDialog::kHFMinimal: - splitHeader = false; - msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf), - "%s %5d- %s", - timeBuf, pLogMessage->GetPid(), pLogMessage->GetMsg()); - break; - case LogPrefsDialog::kHFInternal: - splitHeader = false; - msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf), - "[%s] %s", timeBuf, pLogMessage->GetMsg()); - break; - default: - fprintf(stderr, "Sim: unexpected header format %d\n", headerFmt); - assert(false); - break; - } - - if (msgLen < 0) { - fprintf(stderr, "WHOOPS\n"); - assert(outBuf == msgBuf); - return; - } - - if (splitHeader) { - if (mUseColor) - pTextCtrl->SetDefaultStyle(wxTextAttr(*wxLIGHT_GREY)); - pTextCtrl->AppendText(wxString::FromAscii(outBuf)); - if (mUseColor) - pTextCtrl->SetDefaultStyle(msgColor); - pTextCtrl->AppendText(wxString::FromAscii(pLogMessage->GetMsg())); - if (mUseColor) - pTextCtrl->SetDefaultStyle(*wxBLACK); - pTextCtrl->AppendText(wxString::FromAscii(finalNewlines)); - } else { - if (mUseColor) - pTextCtrl->SetDefaultStyle(msgColor); - pTextCtrl->AppendText(wxString::FromAscii(outBuf)); - if (mUseColor) - pTextCtrl->SetDefaultStyle(*wxBLACK); - pTextCtrl->AppendText(wxString::FromAscii(finalNewlines)); - } - - /* if we allocated storage for this message, free it */ - if (outBuf != msgBuf) - free(outBuf); -} - -/* - * Write the message to the log file. - * - * We can't just do this in FormatMessage(), because that re-writes all - * messages on the display whenever the output format or filter changes. - * - * Use a one-log-per-line format here to make "grep" useful. - */ -void LogWindow::LogToFile(const LogMessage* pLogMessage) -{ -#if defined(HAVE_LOCALTIME_R) - struct tm tmBuf; -#endif - struct tm* ptm; - char timeBuf[32]; - char msgBuf[256]; - int msgLen; - char* outBuf; - char priChar; - - assert(mLogFp != NULL); - - time_t when = pLogMessage->GetWhen(); -#if defined(HAVE_LOCALTIME_R) - ptm = localtime_r(&when, &tmBuf); -#else - ptm = localtime(&when); -#endif - - strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - priChar = ((const char*)GetPriorityString(pLogMessage->GetPriority()))[0]; - - outBuf = msgBuf; - if (pLogMessage->GetInternal()) { - msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf), - "[%s %5d *] %s\n", - timeBuf, pLogMessage->GetPid(), pLogMessage->GetMsg()); - } else { - msgLen = android_snprintfBuffer(&outBuf, sizeof(msgBuf), - "[%s %5d %c] %s)\n", - timeBuf, pLogMessage->GetPid(), priChar, - pLogMessage->GetMsg()); - } - if (fwrite(outBuf, msgLen, 1, mLogFp) != 1) - fprintf(stderr, "Sim: WARNING: partial log write\n"); - fflush(mLogFp); - - /* if we allocated storage for this message, free it */ - if (outBuf != msgBuf) - free(outBuf); -} - -/* - * Get the modification date of a file. - */ -static bool GetFileModDate(const char* fileName, time_t* pModWhen) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) - return false; - - *pModWhen = sb.st_mtime; - return true; -} - -/* - * Open or close the log file as appropriate. - */ -void LogWindow::PrepareLogFile(void) -{ - const int kLogFileMaxAge = 8 * 60 * 60; // 8 hours - - if (!mWriteFile && mLogFp != NULL) { - printf("Sim: closing log file\n"); - fclose(mLogFp); - mLogFp = NULL; - } else if (mWriteFile && mLogFp == NULL) { - printf("Sim: opening log file '%s'\n", (const char*)mFileName.ToAscii()); - time_t now, modWhen = 0; - const char* openFlags; - - now = time(NULL); - if (!mTruncateOld || - (GetFileModDate(mFileName.ToAscii(), &modWhen) && - modWhen + kLogFileMaxAge > now)) - { - if (modWhen != 0) { - printf("--- log file is %.3f hours old, appending\n", - (now - modWhen) / 3600.0); - } - openFlags = "a"; // open for append (text mode) - } else { - if (modWhen != 0) { - printf("--- log file is %.3f hours old, truncating\n", - (now - modWhen) / 3600.0); - } - openFlags = "w"; // open for writing, truncate (text mode) - } - - mLogFp = fopen(mFileName.ToAscii(), openFlags); - if (mLogFp == NULL) { - fprintf(stderr, "Sim: failed opening log file '%s': %s\n", - (const char*) mFileName.ToAscii(), strerror(errno)); - } else { - fprintf(mLogFp, "\n\n"); - fflush(mLogFp); - } - } -} - -/* - * Add a new log message. - * - * This function can be called from any thread. It makes a copy of the - * stuff in "*pBundle" and sends it to the main UI thread. - */ -/*static*/ void LogWindow::PostLogMsg(const android_LogBundle* pBundle) -{ - LogMessage* pNewMessage = LogMessage::Create(pBundle); - - SendToWindow(pNewMessage); -} - -/* - * Post a simple string to the log. - */ -/*static*/ void LogWindow::PostLogMsg(const char* msg) -{ - LogMessage* pNewMessage = LogMessage::Create(msg); - - SendToWindow(pNewMessage); -} - -/* - * Post a simple wxString to the log. - */ -/*static*/ void LogWindow::PostLogMsg(const wxString& msg) -{ - LogMessage* pNewMessage = LogMessage::Create(msg.ToAscii()); - - SendToWindow(pNewMessage); -} - -/* - * Send a log message to the log window. - */ -/*static*/ void LogWindow::SendToWindow(LogMessage* pMessage) -{ - if (pMessage != NULL) { - wxWindow* pMainFrame = ((MyApp*)wxTheApp)->GetMainFrame(); - UserEventMessage* pUem = new UserEventMessage; - pUem->CreateLogMessage(pMessage); - - UserEvent uev(0, (void*) pUem); - - pMainFrame->AddPendingEvent(uev); - } else { - fprintf(stderr, "Sim: failed to add new log message\n"); - } -} - - -/* - * This is a sanity check. We need to stop somewhere to avoid trashing - * the system on bad input. - */ -#define kMaxLen 65536 - -#define VSNPRINTF vsnprintf // used to worry about _vsnprintf - - -/* - * Print a formatted message into a buffer. Pass in a buffer to try to use. - * - * If the buffer isn't big enough to hold the message, allocate storage - * with malloc() and return that instead. The caller is responsible for - * freeing the storage. - * - * Returns the length of the string, or -1 if the printf call failed. - */ -static int android_vsnprintfBuffer(char** pBuf, int bufLen, const char* format, va_list args) -{ - int charsOut; - char* localBuf = NULL; - - assert(pBuf != NULL && *pBuf != NULL); - assert(bufLen > 0); - assert(format != NULL); - - while (1) { - /* - * In some versions of libc, vsnprintf only returns 0 or -1, where - * -1 indicates the the buffer wasn't big enough. In glibc 2.1 - * and later, it returns the actual size needed. - * - * MinGW is just returning -1, so we have to retry there. - */ - char* newBuf; - - charsOut = VSNPRINTF(*pBuf, bufLen, format, args); - - if (charsOut >= 0 && charsOut < bufLen) - break; - - //fprintf(stderr, "EXCEED: %d vs %d\n", charsOut, bufLen); - if (charsOut < 0) { - /* exact size not known, double previous size */ - bufLen *= 2; - if (bufLen > kMaxLen) - goto fail; - } else { - /* exact size known, just use that */ - - bufLen = charsOut + 1; - } - //fprintf(stderr, "RETRY at %d\n", bufLen); - - newBuf = (char*) realloc(localBuf, bufLen); - if (newBuf == NULL) - goto fail; - *pBuf = localBuf = newBuf; - } - - // On platforms where snprintf() doesn't return the number of - // characters output, we would need to call strlen() here. - - return charsOut; - -fail: - if (localBuf != NULL) { - free(localBuf); - *pBuf = NULL; - } - return -1; -} - -/* - * Variable-arg form of the above. - */ -static int android_snprintfBuffer(char** pBuf, int bufLen, const char* format, ...) -{ - va_list args; - int result; - - va_start(args, format); - result = android_vsnprintfBuffer(pBuf, bufLen, format, args); - va_end(args); - - return result; -} - - |
