📄 osutils.cxx
字号:
/*
* osutils.cxx
*
* Operating System utilities.
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Revision: 19649 $
* $Author: csoutheren $
* $Date: 2008-03-03 02:04:00 +0000 (Mon, 03 Mar 2008) $
*/
#include <ptlib.h>
#include <vector>
#include <map>
#include <ctype.h>
#include <ptlib/pfactory.h>
#include <ptlib/pprocess.h>
#ifdef _WIN32
#include <ptlib/msos/ptlib/debstrm.h>
#endif
#ifdef __MACOSX__
namespace PWLibStupidOSXHacks {
extern int loadShmVideoStuff;
extern int loadCoreAudioStuff;
extern int loadFakeVideoStuff;
};
#endif
class PSimpleThread : public PThread
{
PCLASSINFO(PSimpleThread, PThread);
public:
PSimpleThread(
const PNotifier & notifier,
INT parameter,
AutoDeleteFlag deletion,
Priority priorityLevel,
const PString & threadName,
PINDEX stackSize
);
void Main();
protected:
PNotifier callback;
INT parameter;
};
#ifndef __NUCLEUS_PLUS__
static ostream * PErrorStream = &cerr;
#else
static ostream * PErrorStream = NULL;
#endif
ostream & PGetErrorStream()
{
return *PErrorStream;
}
void PSetErrorStream(ostream * s)
{
#ifndef __NUCLEUS_PLUS__
PErrorStream = s != NULL ? s : &cerr;
#else
PErrorStream = s;
#endif
}
//////////////////////////////////////////////////////////////////////////////
#if PTRACING
class PTraceInfo
{
/* NOTE you cannot have any complex types in this structure. Anything
that might do an asert or PTRACE will crash due to recursion.
*/
public:
unsigned currentLevel;
unsigned options;
unsigned thresholdLevel;
const char * filename;
ostream * stream;
PTimeInterval startTick;
const char * rolloverPattern;
unsigned lastDayOfYear;
ios::fmtflags oldStreamFlags;
std::streamsize oldPrecision;
#if defined(_WIN32)
CRITICAL_SECTION mutex;
void InitMutex() { InitializeCriticalSection(&mutex); }
void Lock() { EnterCriticalSection(&mutex); }
void Unlock() { LeaveCriticalSection(&mutex); }
#elif defined(P_PTHREADS) && P_HAS_RECURSIVE_MUTEX
pthread_mutex_t mutex;
void InitMutex() {
// NOTE this should actually guard against various errors
// returned.
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,
#if P_HAS_RECURSIVE_MUTEX == 2
PTHREAD_MUTEX_RECURSIVE
#else
PTHREAD_MUTEX_RECURSIVE_NP
#endif
);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
void Lock() { pthread_mutex_lock(&mutex); }
void Unlock() { pthread_mutex_unlock(&mutex); }
#else
PMutex * mutex;
void InitMutex() { mutex = new PMutex; }
void Lock() { mutex->Wait(); }
void Unlock() { mutex->Signal(); }
#endif
PTraceInfo()
: currentLevel(0)
, filename(NULL)
#ifdef __NUCLEUS_PLUS__
, stream(NULL)
#else
, stream(&cerr)
#endif
, startTick(PTimer::Tick())
, rolloverPattern("yyyy_MM_dd")
, lastDayOfYear(0)
, oldStreamFlags(ios::left)
, oldPrecision(0)
{
InitMutex();
const char * env = getenv("PWLIB_TRACE_STARTUP"); // Backward compatibility test
if (env == NULL)
env = getenv("PTLIB_TRACE_STARTUP"); // Backward compatibility test
if (env != NULL) {
thresholdLevel = atoi(env);
options = PTrace::Blocks | PTrace::Timestamp | PTrace::Thread | PTrace::FileAndLine;
}
else {
env = getenv("PWLIB_TRACE_LEVEL");
if (env == NULL)
env = getenv("PTLIB_TRACE_LEVEL");
thresholdLevel = env != NULL ? atoi(env) : 0;
env = getenv("PWLIB_TRACE_OPTIONS");
if (env == NULL)
env = getenv("PTLIB_TRACE_OPTIONS");
options = env != NULL ? atoi(env) : PTrace::FileAndLine;
}
env = getenv("PWLIB_TRACE_FILE");
if (env == NULL)
env = getenv("PTLIB_TRACE_FILE");
OpenTraceFile(env);
}
static PTraceInfo & Instance()
{
static PTraceInfo info;
return info;
}
void SetStream(ostream * newStream)
{
#ifndef __NUCLEUS_PLUS__
if (newStream == NULL)
newStream = &cerr;
#endif
Lock();
if (stream != &cerr && stream != &cout)
delete stream;
stream = newStream;
Unlock();
}
void OpenTraceFile(const char * newFilename)
{
if (newFilename != NULL)
filename = newFilename;
if (filename == NULL)
return;
PMEMORY_IGNORE_ALLOCATIONS_FOR_SCOPE;
if (strcasecmp(filename, "stderr") == 0)
SetStream(&cerr);
else if (strcasecmp(filename, "stdout") == 0)
SetStream(&cout);
#ifdef _WIN32
else if (strcasecmp(filename, "DEBUGSTREAM") == 0)
SetStream(new PDebugStream);
#endif
else {
PFilePath fn(filename);
fn.Replace("%P", PString((unsigned int) PProcess::Current().GetProcessID()));
if ((options & PTrace::RotateDaily) != 0)
{
PTime now;
fn = PFilePath(fn.GetDirectory() + fn.GetTitle() + now.AsString(rolloverPattern, ((options&PTrace::GMTTime) ? PTime::GMT : PTime::Local)) + fn.GetType());
}
PTextFile * traceOutput;
if (options & PTrace::AppendToFile) {
traceOutput = new PTextFile(fn, PFile::ReadWrite);
traceOutput->SetPosition(0, PFile::End);
}
else
traceOutput = new PTextFile(fn, PFile::WriteOnly);
if (traceOutput->IsOpen())
SetStream(traceOutput);
else {
PTRACE(0, PProcess::Current().GetName() << "Could not open trace output file \"" << fn << '"');
delete traceOutput;
}
}
}
};
void PTrace::SetStream(ostream * s)
{
PTraceInfo::Instance().SetStream(s);
}
void PTrace::Initialise(
unsigned level,
const char * filename,
unsigned options
)
{
Initialise(level, filename, NULL, options);
}
void PTrace::Initialise(unsigned level, const char * filename, const char * rolloverPattern, unsigned options)
{
PTraceInfo & info = PTraceInfo::Instance();
info.options = options;
info.thresholdLevel = level;
info.rolloverPattern = rolloverPattern != NULL ? rolloverPattern : "yyyy_MM_dd";
// Does PTime::GetDayOfYear() etc. want to take zone param like PTime::AsString() to switch
// between os_gmtime and os_localtime?
info.lastDayOfYear = (options & RotateDaily) != 0 ? PTime().GetDayOfYear() : 0;
info.OpenTraceFile(filename);
#if PTRACING
PProcess & process = PProcess::Current();
Begin(0, "", 0) << "\tVersion " << process.GetVersion(PTrue)
<< " by " << process.GetManufacturer()
<< " on " << process.GetOSClass() << ' ' << process.GetOSName()
<< " (" << process.GetOSVersion() << '-' << process.GetOSHardware()
<< ") at " << PTime().AsString("yyyy/M/d h:mm:ss.uuu")
<< End;
#endif
}
void PTrace::SetOptions(unsigned options)
{
PTraceInfo::Instance().options |= options;
}
void PTrace::ClearOptions(unsigned options)
{
PTraceInfo::Instance().options &= ~options;
}
unsigned PTrace::GetOptions()
{
return PTraceInfo::Instance().options;
}
void PTrace::SetLevel(unsigned level)
{
PTraceInfo::Instance().thresholdLevel = level;
}
unsigned PTrace::GetLevel()
{
return PTraceInfo::Instance().thresholdLevel;
}
PBoolean PTrace::CanTrace(unsigned level)
{
return level <= PTraceInfo::Instance().thresholdLevel;
}
ostream & PTrace::Begin(unsigned level, const char * fileName, int lineNum)
{
PTraceInfo & info = PTraceInfo::Instance();
if (level == UINT_MAX)
return *info.stream;
info.Lock();
if ((info.filename != NULL) && (info.options&RotateDaily) != 0) {
unsigned day = PTime().GetDayOfYear();
if (day != info.lastDayOfYear) {
info.OpenTraceFile(NULL);
info.lastDayOfYear = day;
if (info.stream == NULL)
info.SetStream(&cerr);
}
}
PThread * thread = PThread::Current();
if (thread != NULL)
thread->traceStreams.Push(new PStringStream);
ostream & stream = thread != NULL ? (ostream &)thread->traceStreams.Top() : *info.stream;
info.oldStreamFlags = stream.flags();
info.oldPrecision = stream.precision();
// Before we do new trace, make sure we clear any errors on the stream
stream.clear();
if ((info.options&SystemLogStream) == 0) {
if ((info.options&DateAndTime) != 0) {
PTime now;
stream << now.AsString("yyyy/MM/dd hh:mm:ss.uuu\t", (info.options&GMTTime) ? PTime::GMT : PTime::Local);
}
if ((info.options&Timestamp) != 0)
stream << setprecision(3) << setw(10) << (PTimer::Tick()-info.startTick) << '\t';
if ((info.options&Thread) != 0) {
if (thread == NULL)
stream << "ThreadID=0x"
<< setfill('0') << hex << setw(8)
<< PThread::GetCurrentThreadId()
<< setfill(' ') << dec;
else {
PString name = thread->GetThreadName();
if (name.GetLength() <= 23)
stream << setw(23) << name;
else
stream << name.Left(10) << "..." << name.Right(10);
}
stream << '\t';
}
if ((info.options&ThreadAddress) != 0)
stream << hex << setfill('0')
<< setw(7) << (void *)PThread::Current()
<< dec << setfill(' ') << '\t';
}
if ((info.options&TraceLevel) != 0)
stream << level << '\t';
if ((info.options&FileAndLine) != 0 && fileName != NULL) {
const char * file = strrchr(fileName, '/');
if (file != NULL)
file++;
else {
file = strrchr(fileName, '\\');
if (file != NULL)
file++;
else
file = fileName;
}
stream << setw(16) << file << '(' << lineNum << ")\t";
}
// Save log level for this message so End() function can use. This is
// protected by the PTraceMutex or is thread local
if (thread == NULL)
info.currentLevel = level;
else {
thread->traceLevel = level;
info.Unlock();
}
return stream;
}
ostream & PTrace::End(ostream & paramStream)
{
PTraceInfo & info = PTraceInfo::Instance();
paramStream.flags(info.oldStreamFlags);
paramStream.precision(info.oldPrecision);
PThread * thread = PThread::Current();
if (thread != NULL) {
PStringStream * stackStream = thread->traceStreams.Pop();
PAssert(¶mStream == stackStream, PLogicError);
info.Lock();
*info.stream << *stackStream;
delete stackStream;
}
else {
PAssert(¶mStream == info.stream, PLogicError);
}
if ((info.options&SystemLogStream) != 0) {
// Get the trace level for this message and set the stream width to that
// level so that the PSystemLog can extract the log level back out of the
// ios structure. There could be portability issues with this though it
// should work pretty universally.
info.stream->width((thread != NULL ? thread->traceLevel : info.currentLevel) + 1);
info.stream->flush();
}
else
*info.stream << endl;
info.Unlock();
return paramStream;
}
PTrace::Block::Block(const char * fileName, int lineNum, const char * traceName)
{
file = fileName;
line = lineNum;
name = traceName;
if ((PTraceInfo::Instance().options&Blocks) != 0) {
PThread * thread = PThread::Current();
thread->traceBlockIndentLevel += 2;
ostream & s = PTrace::Begin(1, file, line);
s << "B-Entry\t";
for (unsigned i = 0; i < thread->traceBlockIndentLevel; i++)
s << '=';
s << "> " << name << PTrace::End;
}
}
PTrace::Block::~Block()
{
if ((PTraceInfo::Instance().options&Blocks) != 0) {
PThread * thread = PThread::Current();
ostream & s = PTrace::Begin(1, file, line);
s << "B-Exit\t<";
for (unsigned i = 0; i < thread->traceBlockIndentLevel; i++)
s << '=';
s << ' ' << name << PTrace::End;
thread->traceBlockIndentLevel -= 2;
}
}
#endif // PTRACING
///////////////////////////////////////////////////////////////////////////////
// PDirectory
void PDirectory::CloneContents(const PDirectory * d)
{
CopyContents(*d);
}
///////////////////////////////////////////////////////////////////////////////
// PTimeInterval
DWORD PTimeInterval::GetInterval() const
{
if (milliseconds <= 0)
return 0;
if (milliseconds >= UINT_MAX)
return UINT_MAX;
return (DWORD)milliseconds;
}
///////////////////////////////////////////////////////////////////////////////
// PTimer
PTimer::PTimer(long millisecs, int seconds, int minutes, int hours, int days)
: resetTime(millisecs, seconds, minutes, hours, days)
{
Construct();
}
PTimer::PTimer(const PTimeInterval & time)
: resetTime(time)
{
Construct();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -