環境
環境Windows Visual Stdudio 2019 VC++
デバックプリント
ファイル名と行数をプリント出す場合C言語では以下
C#の場合「StackFrame」クラスを利用すると一つ前の関数の情報を呼び出せる。
これ便利だなぁと思って、VC++でSakuraMyStackFrameクラスを作成し
桜エディタで、以下のようなログを出力するようにしてみた。
出力形式:タイムスタンプ+ファイル名:関数名(行数)「・・・ログ・・・」
[2020/10/31 07:56:04] WinMain.cpp:wWinMain(94) -- -- WinMain -- --
- C/C++言語 現在の関数の情報を出力
printf("%s(%d) hogehoge\n",__func__,__LINE__);- C#の場合「StackFrame」クラスを利用
1つ前の場合 StackFrame(1, true)
2つ前の場合 StackFrame(2, true)
publicstaticvoidout(stringbuf){stringpath=Directory.GetCurrentDirectory()+"debuglog.txt";DateTimedt=DateTime.Now;stringtimebuf=dt.ToString("yyyy/MM/dd HH:mm:ss ");//「一つ前の」スタックを参照するStackFrameCallStack=newStackFrame(1,true);stringsourceline="";stringfname="";if(CallStack.GetFileName().Length>0){fname=Path.GetFileName(CallStack.GetFileName());}sourceline=fname+":"+CallStack.GetMethod().Name+"("+CallStack.GetFileLineNumber()+")";stringlinebuf=timebuf+sourceline+buf+"\r\n";File.AppendAllText(path,linebuf);}桜エディタにクラスを追加する
桜エディタのソースコード https://github.com/sakura-editor/sakura
のsakura_coreにmyutilフォルダ追加し以下のクラスを作成する。
SakuraMyStackFrameクラス ・・・関数のスタックを取得する
SakuraMyLogクラス ・・・ログ出力に関するクラス
SakuraMyStrクラス ・・・文字操作クラス
コールスタッフ情報を_stackに保存し、取り出しは_frameを指定する。
vector < MYSTACKFRAMES> _stack;
- SakuraMyStackFrameクラス
#ifndef _INCLUDESakuraMyStackFrame_H_
#define _INCLUDESakuraMyStackFrame_H_
#pragma once
#pragma comment(lib, "dbghelp.lib") //コールスタック利用
#include <cstddef>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <iostream>
#include <list>
#include <vector>
#include <windows.h> //MultiByteToWideChar
#include <tchar.h> // _T
usingnamespacestd;classMyException{public:CONTEXTContext;MyException(){RtlCaptureContext(&Context);}};typedefstruct_STACKFRAMES{std::stringSymbolName;ULONG64SymbolAddress;std::stringlineFileName;ULONG64lineLineNumber;std::stringmodule;}MYSTACKFRAMES;classSakuraMyStackFrame{private://コールスタックの取り出す番号指定int_frame;//コールスタックを保持vector<MYSTACKFRAMES>_stack;public:SakuraMyStackFrame(){}SakuraMyStackFrame(intframe){_frame=frame;_stack.clear();MyExceptione;printStack(&e.Context);//backtrace(false);}~SakuraMyStackFrame(){_stack.clear();}//voidSetframe(intf){_frame=f;};//コールスタックstringbacktrace();//メソッドstd::stringGetMethodName();ULONG64GetAddress();std::stringGetFileFullName();//ファイル名のフルパスstd::stringGetFileName();//ファイル名のみULONG64GetFileLineNumber();//行番号//スタックトレースvoidprintStack(CONTEXT*ctx);};#endif // !_INCLUDESakuraMyStackFrame_H_
//ファイル内で次のすべての警告をオフ#pragma warning(disable : 4996)
#include "StdAfx.h" //先頭で定義
#include "SakuraMyStackFrame.h"
#include <time.h>
#if !SAKURA
#include <DbgHelp.h>
#endif
#include <vector>
stringSakuraMyStackFrame::backtrace(){// backtrace(true);stringrlt="";vector<MYSTACKFRAMES>::iteratoritr=_stack.begin();intcnt=0;while(itr!=_stack.end()){stringszText;sprintf((char*)szText.c_str(),"%02d\tat 関数名:%s ファイル名 %s: 行番号: %lu: アドレス:0x%0x \n",cnt,itr->SymbolName.c_str(),itr->lineFileName.c_str(),itr->lineLineNumber,itr->SymbolAddress);itr++;cnt++;rlt=rlt+szText;}returnrlt;}std::stringSakuraMyStackFrame::GetMethodName(){std::stringrlt="";if(_stack.size()==0||_stack.size()<=_frame){returnrlt;}MYSTACKFRAMESs=_stack[_frame];returns.SymbolName;}ULONG64SakuraMyStackFrame::GetAddress(){std::stringrlt="";if(_stack.size()==0||_stack.size()<=_frame){return0;}MYSTACKFRAMESs=_stack[_frame];returns.SymbolAddress;}std::stringSakuraMyStackFrame::GetFileFullName(){std::stringrlt="";if(_stack.size()==0||_stack.size()<=_frame){returnrlt;}MYSTACKFRAMESs=_stack[_frame];returns.lineFileName;}std::stringSakuraMyStackFrame::GetFileName(){std::stringrlt="";if(_stack.size()==0||_stack.size()<=_frame){returnrlt;}MYSTACKFRAMESs=_stack[_frame];intp1=s.lineFileName.rfind("\\");intlen=s.lineFileName.length();rlt=s.lineFileName;if(p1!=-1){rlt=rlt.substr(p1+1,len-p1-1);}returnrlt;}ULONG64SakuraMyStackFrame::GetFileLineNumber(){std::stringrlt="";if(_stack.size()==0||_stack.size()<=_frame){return0;}MYSTACKFRAMESs=_stack[_frame];returns.lineLineNumber;}voidSakuraMyStackFrame::printStack(CONTEXT*ctx)//Prints stack trace based on context record{BOOLresult;HANDLEprocess;HANDLEthread;HMODULEhModule;STACKFRAME64stack;ULONGframe;DWORD64displacement;DWORDdisp;IMAGEHLP_LINE64*line;constintMaxNameLen=256;charbuffer[sizeof(SYMBOL_INFO)+MAX_SYM_NAME*sizeof(TCHAR)];charname[MaxNameLen];charmodule[MaxNameLen];PSYMBOL_INFOpSymbol=(PSYMBOL_INFO)buffer;memset(&stack,0,sizeof(STACKFRAME64));process=GetCurrentProcess();thread=GetCurrentThread();displacement=0;#if !defined(_M_AMD64)
stack.AddrPC.Offset=(*ctx).Eip;stack.AddrPC.Mode=AddrModeFlat;stack.AddrStack.Offset=(*ctx).Esp;stack.AddrStack.Mode=AddrModeFlat;stack.AddrFrame.Offset=(*ctx).Ebp;stack.AddrFrame.Mode=AddrModeFlat;#endif
SymInitialize(process,NULL,TRUE);//load symbols_stack.clear();for(frame=0;;frame++){//リスト追加用MYSTACKFRAMESstackf;//get next call from stackresult=StackWalk64(#if defined(_M_AMD64)
IMAGE_FILE_MACHINE_AMD64#else
IMAGE_FILE_MACHINE_I386#endif
,process,thread,&stack,ctx,NULL,SymFunctionTableAccess64,SymGetModuleBase64,NULL);if(!result)break;//get symbol name for addresspSymbol->SizeOfStruct=sizeof(SYMBOL_INFO);pSymbol->MaxNameLen=MAX_SYM_NAME;SymFromAddr(process,(ULONG64)stack.AddrPC.Offset,&displacement,pSymbol);line=(IMAGEHLP_LINE64*)malloc(sizeof(IMAGEHLP_LINE64));line->SizeOfStruct=sizeof(IMAGEHLP_LINE64);//try to get lineif(SymGetLineFromAddr64(process,stack.AddrPC.Offset,&disp,line)){printf(" at %s in %s: line: %lu: address: 0x%0X \n",pSymbol->Name,line->FileName,line->LineNumber,pSymbol->Address);stackf.SymbolAddress=pSymbol->Address;stackf.SymbolName=pSymbol->Name;stackf.lineFileName=line->FileName;stackf.lineLineNumber=line->LineNumber;stackf.module="";}else{//failed to get lineprintf(" at %s, address 0x%0X.",pSymbol->Name,pSymbol->Address);hModule=NULL;lstrcpyA(module,"");GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,(LPCTSTR)(stack.AddrPC.Offset),&hModule);//at least print module nameif(hModule!=NULL)GetModuleFileNameA(hModule,module,MaxNameLen);printf("in %s\n",module);stackf.SymbolAddress=pSymbol->Address;stackf.SymbolName=pSymbol->Name;stackf.lineFileName="";stackf.lineLineNumber=0;stackf.module=module;}_stack.push_back(stackf);free(line);line=NULL;}}- SakuraMyLogクラス
#pragma once
#ifndef _INCLUDESakuraMyLog_H_
#define _INCLUDESakuraMyLog_H_
//#pragma comment(lib, "mscorlib.lib")#if SAKURA
#include "SakuraMyStr.h"
#include "SakuraMyStackFrame.h"
#else
#include "SakuraMyStr.h"
#include "SakuraMyStackFrame.h"
#endif
#pragma region "コールスタック"
//https://raw.githubusercontent.com/gentilkiwi/mimikatz/110a831ebe7b529c5dd3010f9e7fced0d3e3a46c/inc/DbgHelp.h#include <windows.h>
//#include <dbghelp.h> //ADDRESS KDHELP//C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\DbgHelp.h//に定義済み#pragma endregion
//#define TO_STR( param ) #param//#define PRINT( ... ) printf( __VA_ARGS__ )//#define TO_STR_VA( ... ) #__VA_ARGS__//#define JOIN( x, y ) x ## y//#define func(const char* format, ...) funcDebug(__LINE__, __func__, __FILE__, const char* format, ...)//int funcDebug(int line, char* funcname, char* filename, const char* format, ...) {// //}usingnamespacestd;classSakuraMyLog{private:constchar*PATH_FILE_DEBUG="debug.out";int_frame;public:SakuraMyLog(){_frame=3;}voidSetframe(intf){_frame=f;}std::stringloghead();voiddebugout(constchar*format,...);voiddebugoutW(std::stringbuf);char*GetCurrentPath();};#endif // !_INCLUDESakuraMyLog_H_
保存するファイルパスは適当に変更して下さい
mystr->writeW("G:\plog\vs2019\2020_1022_UtilDLL\sakuradebug.txt", buf);
#include "StdAfx.h" //先頭に追加 C1010対応
#include "SakuraMyLog.h"
#include <stdio.h>
#include <tchar.h> // _T
#include <string>
#include <io.h>
#include <csignal>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
std::stringSakuraMyLog::loghead(){std::stringrlt="";SakuraMyStr*mystr=newSakuraMyStr();SakuraMyStackFrame*mystack=newSakuraMyStackFrame(_frame);charline[10240];chartm[255];strcpy(tm,mystr->timestamp(1));if(mystack->GetFileName().length()>0&&mystack->GetMethodName().length()>0){sprintf(line,"[%s] %s:%s(%d) ",tm,mystack->GetFileName().c_str(),mystack->GetMethodName().c_str(),mystack->GetFileLineNumber());}elseif(mystack->GetMethodName().length()>0){sprintf(line,"[%s] %s(%d) ",tm,mystack->GetMethodName().c_str(),mystack->GetFileLineNumber());}else{sprintf(line,"[%s] -(%d) ",tm,mystack->GetFileLineNumber());}rlt=line;free(mystr);free(mystack);returnrlt;}voidSakuraMyLog::debugout(constchar*fmt,...){SakuraMyStr*mystr=newSakuraMyStr();//mystack->backtrace();charline[10240];try{strcpy(line,loghead().c_str());intsize=strlen(line);va_listap;// 可変長引数を1個の変数にまとめるva_start(ap,fmt);// まとめられた変数で処理するvsprintf(&line[size],fmt,ap);va_end(ap);// 戻り値省略mystr->write("G:\\plog\\vs2019\\2020_1022_UtilDLL\\sakuradebug.txt",(constchar*)line);free(mystr);}catch(...){std::cout<<"err"<<::GetLastError()<<std::endl;}}voidSakuraMyLog::debugoutW(std::stringbuf){SakuraMyStr*mystr=newSakuraMyStr();try{//保存するファイルパスmystr->writeW("G:\\plog\\vs2019\\2020_1022_UtilDLL\\sakuradebug.txt",buf);free(mystr);}catch(...){std::cout<<"err"<<::GetLastError()<<std::endl;}}char*SakuraMyLog::GetCurrentPath(){charrlt[1024];TCHARszBuf[MAX_PATH];(void)GetCurrentDirectory(_countof(szBuf),szBuf);sprintf(rlt,"%s\\%s",szBuf,PATH_FILE_DEBUG);returnrlt;}- SakuraMyStrクラス
#pragma once
#ifndef _INCLUDE_SakuraMyStr_H_
#define _INCLUDE_SakuraMyStr_H_
#include <cstddef>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <iostream>
#include <list>
#include <fstream> // ifstream, ofstream
#include <vector>
#include <string>
//#include <windows.h> //MultiByteToWideChar
#include <tchar.h> // _T
//C++で静的配列の要素数を求めるテンプレート関数#define COUNT(arry)(sizeof(arry) / sizeof(arry[0]));
usingnamespacestd;#define MAX_BUF 0x7FFFFFFF
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //// 定数 //// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //// SJISのコードページ(CP_ACP では無くこれを使えばおそらく英語版Winでも動くはず。) 2008/5/12 Uchi#define CP_SJIS 932
classSakuraMyStr{private:public:SakuraMyStr(){}~SakuraMyStr(){}//書き込みboolwrite(constchar*path,constchar*buff);boolwriteW(std::string,conststd::stringbuff);//タイムスタンプchar*timestamp(inttype);};#endif // !
//ファイル内で次のすべての警告をオフ#pragma warning(disable : 4996)
#include "StdAfx.h" //先頭で定義
#include "SakuraMyStr.h"
#include <time.h>
#if SAKURA
#include <assert.h>
#else
#include <DbgHelp.h>
#include <cassert>
#endif
/*
モード 働き
"r" 読み出し(のみの)モードでファイルをオープンする。ファイルが存在しないときはオープンできない。
"w" 書き込み(のみの)モードでファイルをオープンする。ファイルがなければ作成し、存在していれば最初にその内容を空にする。
"a" 追加モードでファイルをオープンする。ファイルがなければ作成し、存在していれば、現在の内容はそのままで、ファイル末尾に追加書き込みが行われる。
"r+" 読み書き両用のモードでファイルをオープンする。ファイルが存在しないときはオープンできない。
"w+" 読み書き両用のモードでファイルをオープンする。ファイルがなければ作成し、存在していれば最初にその内容を空にする。
"a+" 読み書き両用のモードでファイルをオープンする。ファイルがなければ作成し、存在していれば、現在の内容はそのままで、ファイル末尾に追加書き込みが行われる。
*/boolSakuraMyStr::write(constchar*path,constchar*buff){FILE*fout;// 出力ストリームfout=fopen(path,"a");fprintf(fout,"%s",buff);fclose(fout);returntrue;}//SJIS保存boolSakuraMyStr::writeW(std::stringpath,conststd::stringbuff){std::ofstreamwriting_file;writing_file.open(path,std::ios::app);writing_file<<buff;returntrue;}char*SakuraMyStr::timestamp(inttype){time_ttimer;/* 時刻を取り出すための型(実際はunsigned long型) */structtm*local;/* tm構造体(時刻を扱う *//* 年月日と時分秒保存用 */intyear,month,day,hour,minute,second;timer=time(NULL);/* 現在時刻を取得 */local=localtime(&timer);/* 地方時に変換 *//* 年月日と時分秒をtm構造体の各パラメタから変数に代入 */year=local->tm_year+1900;/* 1900年からの年数が取得されるため */month=local->tm_mon+1;/* 0を1月としているため */day=local->tm_mday;hour=local->tm_hour;minute=local->tm_min;second=local->tm_sec;charline[256];/* 現在の日時を表示 */if(type==0){sprintf(line,"%04d年%02d月%02d日 %02d時%02d分%02d秒",year,month,day,hour,minute,second);}elseif(type==1){sprintf(line,"%04d/%02d/%02d %02d:%02d:%02d",year,month,day,hour,minute,second);}else{sprintf(line,"%04d%02d%02d%02d%02d%02d",year,month,day,hour,minute,second);}returnline;}桜エディタのプロパティ
プロパティ>構成プロパティ>プリプロセッサ>プリプロセッサの定義に SAKURA を追記
桜エディタのプロパティの追加インクルードディレクトリに、新規作成のパスを指定
例:
G:\plog\vs2019\2020_1022_UtilDLL\UtilDLL_master\CplusDLL\Sakura
桜エディタに埋め込む
追加したクラスを利用する
WinMain.cpp の DEBUG_TRACEや、MYTRACEのメッセージを出力するファイルの呼び出し先がDebug1.cpp
//開発情報DEBUG_TRACE(L"-- -- WinMain -- --\n");StdAfx.hを修正する。最終行あたりに以下3行追加
(省略)#include "SakuraMyStr.h"
#include "SakuraMyLog.h"
#include "SakuraMyStackFrame.h"
Debug1.cppに以下を追記
//追加
mylog.Setframe(3); //3つ前の関数を参照している。
std::string log = mylog.loghead(); //Debugログのタイムスタンプとファイル名+行数を作成
log += wcstombs_new(szText); //SJISをUNICODEに変換して連結している。
mylog.debugoutW(log); //出力
/*! @file
@brief デバッグ用関数
@author Norio Nakatani
@date 2001/06/23 N.Nakatani DebugOut()に微妙~な修正
@date 2002/01/17 aroka 型の修正
@date 2013/03/03 Uchi MessageBox用関数を分離
*//*
Copyright (C) 1998-2001, Norio Nakatani
Copyright (C) 2002, aroka
This source code is designed for sakura editor.
Please contact the copyright holder to use this code for other purpose.
*/#include "StdAfx.h"
#include "debug/Debug1.h"
//追加#include "SakuraMyLog.h"
#include <stdio.h>
#include <stdarg.h>
#if defined(_DEBUG) || defined(USE_RELPRINT)
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //// メッセージ出力:実装 //// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ///*! @brief 書式付きデバッガ出力
@param[in] lpFmt printfの書式付き文字列
引数で与えられた情報をDebugStringとして出力する.
*/voidDebugOutW(LPCWSTRlpFmt,...){staticWCHARszText[16000];SakuraMyLogmylog;//追加va_listargList;va_start(argList,lpFmt);//整形intret=_vsnwprintf_s(szText,_TRUNCATE,lpFmt,argList);//出力if(errno!=EINVAL){::OutputDebugStringW(szText);//追加mylog.Setframe(3);std::stringlog=mylog.loghead();log+=wcstombs_new(szText);mylog.debugoutW(log);}//切り捨て対策if(-1==ret&&errno!=ERANGE){::OutputDebugStringW(L"(切り捨てました...)\n");::DebugBreak();intcount=_vscwprintf(lpFmt,argList);autopLargeBuf=std::make_unique<WCHAR[]>(count+1);if(vswprintf(&pLargeBuf[0],count+1,lpFmt,argList)>0)::OutputDebugStringW(&pLargeBuf[0]);}va_end(argList);//ウェイト::Sleep(1);// Norio Nakatani, 2001/06/23 大量にトレースするときのために}#endif // _DEBUG || USE_RELPRINT
こうすると、以下のようなログを出力する.
[2020/10/31 07:56:04] WinMain.cpp:wWinMain(94) -- -- WinMain -- --
もし、意図しないログの場合、mylog.Setframe(3) の値を変更してみて下さい。
以上
参考URL:https://www.366service.com/jp/qa/24585ff2ce46fd450143a4b58ef0af8b



