33DebugIOFlat::OutputStream::OutputStream(
const char *filename,
unsigned maxSize):
34 m_bufferUsed(0), m_nextChar(0)
37 strcpy(m_fileName,filename);
39 m_limitedFileSize=maxSize>0;
40 m_bufferSize=m_limitedFileSize?maxSize:0x10000;
43 if (!m_limitedFileSize)
44 m_fileHandle=CreateFile(m_fileName,GENERIC_WRITE,0,
NULL,CREATE_ALWAYS,
45 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH,
49DebugIOFlat::OutputStream::~OutputStream()
53DebugIOFlat::OutputStream *DebugIOFlat::OutputStream::Create(
const char *filename,
unsigned maxSize)
55 return new (
DebugAllocMemory(
sizeof(OutputStream))) OutputStream(filename,maxSize);
58void DebugIOFlat::OutputStream::Delete(
const char *path)
61 if (!m_limitedFileSize)
62 CloseHandle(m_fileHandle);
68 char *ext=strrchr(m_fileName,
'.');
70 ext=m_fileName+strlen(m_fileName);
71 char *fileNameOnly=strrchr(m_fileName,
'\\');
72 fileNameOnly=fileNameOnly?fileNameOnly+1:m_fileName;
74 unsigned pathLen=strlen(path);
79 if (path[0]&&(path[1]==
':'||(path[0]==
'\\'&&path[1]==
'\\')))
82 strcpy(help+pathLen,fileNameOnly);
83 help[ext-fileNameOnly+pathLen]=0;
88 strcpy(help,m_fileName);
89 strcpy(help+(fileNameOnly-m_fileName),path);
90 strcpy(help+(fileNameOnly-m_fileName)+pathLen,fileNameOnly);
91 help[ext-fileNameOnly+pathLen+(fileNameOnly-m_fileName)]=0;
94 wsprintf(help+strlen(help),
"(%i)%s",run,ext);
97 if (CopyFile(m_fileName,help,
TRUE))
99 if (GetLastError()!=ERROR_FILE_EXISTS)
107 this->~OutputStream();
111void DebugIOFlat::OutputStream::Write(
const char *src)
116 if (!m_limitedFileSize)
121 unsigned len=strlen(src);
123 while (len>m_bufferSize)
125 InternalWrite(src,m_bufferSize);
129 InternalWrite(src,len);
133void DebugIOFlat::OutputStream::InternalWrite(
const char *src,
unsigned len)
138 if (!m_limitedFileSize)
140 if (m_bufferUsed+len>m_bufferSize)
142 memcpy(m_buffer+m_bufferUsed,src,len);
148 if ((m_bufferUsed+=len)>m_bufferSize)
149 m_bufferUsed=m_bufferSize;
154 if (m_nextChar+len>m_bufferSize)
155 toWrite=m_bufferSize-m_nextChar;
158 memcpy(m_buffer+m_nextChar,src,toWrite);
159 if ((m_nextChar+=toWrite)>=m_bufferSize)
167void DebugIOFlat::OutputStream::Flush(
void)
169 if (!m_limitedFileSize)
173 WriteFile(m_fileHandle,m_buffer,m_bufferUsed,&written,
NULL);
179 m_fileHandle=CreateFile(m_fileName,GENERIC_WRITE,0,
NULL,CREATE_ALWAYS,
180 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH,
183 if (m_bufferUsed<m_bufferSize)
184 WriteFile(m_fileHandle,m_buffer,m_bufferUsed,&written,
NULL);
187 WriteFile(m_fileHandle,m_buffer+m_nextChar,m_bufferUsed-m_nextChar,&written,
NULL);
188 WriteFile(m_fileHandle,m_buffer,m_nextChar,&written,
NULL);
190 CloseHandle(m_fileHandle);
196void DebugIOFlat::ExpandMagic(
const char *src,
const char *splitName,
char *buf)
209 const char *p=strrchr(src,
'.');
212 strncpy(buf,src,p-src);
214 strcpy(buf+(p-src)+1,splitName);
231 if (*src>=
'A'&&*src<=
'Z'&&(*src!=
'N'||splitName))
235 DWORD size=
sizeof(help);
242 GetModuleFileName(
NULL,help,
sizeof(help));
246 GetComputerName(help,&size);
250 GetUserName(help,&size);
256 GetLocalTime(&systime);
258 wsprintf(help,
"%04i%02i%02i-%02i%02i-%02i",
259 systime.wYear,systime.wMonth,systime.wDay,
260 systime.wHour,systime.wMinute,systime.wSecond);
265 if (splitName&&strlen(splitName)<250)
266 strcpy(help,splitName);
272 unsigned len=strlen(help);
282 m_firstStream(
NULL), m_firstSplit(
NULL),
283 m_lastStreamPtr(&m_firstStream), m_lastSplitPtr(&m_firstSplit)
290 for (SplitListEntry *cur=m_firstSplit;cur;)
292 SplitListEntry *kill=cur;
298 for (StreamListEntry *stream=m_firstStream;stream;)
300 StreamListEntry *kill=stream;
302 kill->stream->Delete(m_copyDir);
309 for (SplitListEntry *cur=m_firstSplit;cur;cur=cur->next)
311 if (!(cur->stringTypes&(1<<type)))
315 cur->stream->Write(str);
319 m_firstStream->stream->Write(str);
324 for (StreamListEntry *cur=m_firstStream;cur;cur=cur->next)
325 cur->stream->Flush();
329 unsigned argn,
const char *
const * argv)
331 if (!cmd||!strcmp(cmd,
"help"))
334 dbg <<
"flat I/O help:\n"
335 "The following I/O commands are defined:\n"
336 " add, copy, splitadd, splitview, splitremove\n"
337 "Type in debug.io flat help <cmd> for a detailed command help.\n";
338 else if (!strcmp(argv[0],
"add"))
340 "add [ <filename> [ <size in kb> ] ]\n\n"
341 "Create flat file I/O (optionally specifying file name and file size).\n"
342 "If a filename is specified all output is written to that file. Otherwise\n"
343 "the magic filename '*eMN' is automatically used. Any existing files with\n"
344 "that name are overwritten.\n"
346 "Instead of a real file name a 'magic' file name can be used by starting\n"
347 "the file name with a '*' followed by any number of special characters:\n"
348 "- 'e': inserts EXE name\n"
349 "- 'm': inserts machine name\n"
350 "- 'u': inserts username\n"
351 "- 't': inserts timestamp\n"
352 "- 'n': inserts split name (empty if main log file)\n"
353 "- '-': inserts '-'\n"
354 "- 'E', 'M', 'U', 'T', 'N': same as above but with '-' in front \n"
355 " (for 'N': empty if main log file)\n"
356 ".log is automatically appended if using magic file names.\n"
358 "If a size is specified then all data is internally written to a fixed \n"
359 "size memory based ring buffer. This data is flushed out once the \n"
360 "program exits. If no size is given then the size of the log file is not \n"
361 "limited and any log data is written out immediately.\n";
362 else if (!strcmp(argv[0],
"copy"))
364 "copy <directory>\n\n"
365 "Copies generated log file(s) into the given directory if the program\n"
366 "exists or crashes. If there is already a log file with the same\n"
367 "name a unique number is appended to the current log files' name.\n";
368 else if (!strcmp(argv[0],
"splitadd"))
370 "splitadd <types> <filter> <name> [ <size in kb> ]\n\n"
371 "Splits off part of the log data. Multiple splits can be defined. They \n"
372 "are written out to the first matching split file.\n"
374 "'types' defines one or more string types which should be split off:\n"
380 "- r: replies from commands\n"
381 "- o: other messages \n"
383 "Next a filter is specified that determines which items are to be \n"
384 "filtered (only for string types a, c and l). Items can be listed with\n"
385 "then 'list' command. The filter is exactly specified as in that command.\n"
387 "The third parameter defines a name for this split. If there is \n"
388 "already a split with the same name then both will write to the same \n"
389 "destination file.\n"
391 "Note: If splits are used and the filename given for 'add' is static \n"
392 "or does not contain 'n' or 'N' then the split name is automatically \n"
393 "appended to the log file name (before the extension).\n"
395 "If a size is specified then all data is internally written to a \n"
396 "fixed size memory based ring buffer. This data is flushed out once \n"
397 "the program exits.\n"
399 "If no size is given then the size of the log file is not limited and \n"
400 "any log data is written out immediately.\n";
401 else if (!strcmp(argv[0],
"splitview"))
402 dbg <<
"splitview\n\n"
403 "Shows all existing splits in the order they are evaluated.";
404 else if (!strcmp(argv[0],
"splitremove"))
405 dbg <<
"splitremove <namepattern>\n\n"
406 "Removes all active splits matching the given name pattern.";
408 dbg <<
"Unknown flat I/O command";
410 else if (!strcmp(cmd,
"add"))
415 strcpy(m_baseFilename,argn?argv[0]:
"*eMN");
418 ExpandMagic(m_baseFilename,
NULL,fn);
421 m_firstStream->next=
NULL;
422 m_firstStream->stream=OutputStream::Create(fn,argn>1?atoi(argv[1])*1024:0);
423 m_lastStreamPtr=&m_firstStream->next;
425 else if (!strcmp(cmd,
"copy"))
430 strncpy(m_copyDir,argv[0],
sizeof(m_copyDir)-1);
431 m_copyDir[
sizeof(m_copyDir)-1]=0;
434 else if (!strcmp(cmd,
"splitadd"))
440 SplitListEntry *cur=(SplitListEntry *)
DebugAllocMemory(
sizeof(SplitListEntry));
441 cur->next=*m_lastSplitPtr;
442 m_lastSplitPtr=&cur->next;
446 for (
const char *p=argv[0];*p;++p)
450 case 'a': cur->stringTypes|=1<<
Assert;
break;
451 case 'c': cur->stringTypes|=1<<
Check;
break;
452 case 'l': cur->stringTypes|=1<<
Log;
break;
453 case 'h': cur->stringTypes|=1<<
Crash;
break;
454 case 'x': cur->stringTypes|=1<<
Exception;
break;
455 case 'r': cur->stringTypes|=1<<
CmdReply;
break;
456 case 'o': cur->stringTypes|=1<<
Other;
break;
459 if (!cur->stringTypes)
460 cur->stringTypes=0xffffffff;
462 strncpy(cur->items,argv[1],
sizeof(cur->items)-1);
463 cur->items[
sizeof(cur->items)-1]=0;
465 strncpy(cur->name,argv[2],
sizeof(cur->name)-1);
466 cur->name[
sizeof(cur->name)-1]=0;
470 ExpandMagic(m_baseFilename,cur->name,fn);
471 for (StreamListEntry *stream=m_firstStream;stream;stream=stream->next)
472 if (!strcmp(stream->stream->GetFilename(),fn))
479 *m_lastStreamPtr=stream;
480 m_lastStreamPtr=&stream->next;
481 stream->stream=OutputStream::Create(fn,argn>3?atoi(argv[3])*1024:0);
483 cur->stream=stream->stream;
486 else if (!strcmp(cmd,
"splitview"))
489 for (SplitListEntry *cur=m_firstSplit;cur;cur=cur->next)
497 case Assert: dbg <<
"a";
break;
498 case Check: dbg <<
"c";
break;
499 case Log: dbg <<
"l";
break;
500 case Crash: dbg <<
"h";
break;
503 case Other: dbg <<
"o";
break;
506 dbg <<
" " << cur->items <<
" " << cur->name <<
"\n";
509 else if (!strcmp(cmd,
"splitremove"))
512 const char *pattern=argn<1?
"*":argv[0];
513 for (SplitListEntry **entryPtr=&m_firstSplit;*entryPtr;)
518 SplitListEntry *cur=*entryPtr;
523 entryPtr=&((*entryPtr)->next);
529 for (SplitListEntry *cur=m_firstSplit;cur->next;cur=cur->next);
Debug module main class (singleton).
static bool SimpleMatch(const char *str, const char *pattern)
virtual void Delete(void)
Destroys the current I/O class instance.
virtual void EmergencyFlush(void)
Emergency shutdown function.
static DebugIOInterface * Create(void)
virtual void Write(StringType type, const char *src, const char *str)
Write out some characters differentiated by the log string type.
virtual void Execute(class Debug &dbg, const char *cmd, bool structuredCmd, unsigned argn, const char *const *argv)
I/O class specific command.
StringType
List of possible log string types.
@ Other
some other message
@ CmdReply
Regular command reply.
@ StructuredCmdReply
Structured command reply, see Structured command reply.
void * DebugAllocMemory(unsigned numBytes)
void DebugFreeMemory(void *ptr)