mykedge app android,LogWindow.cpp

//

// 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 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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值