Skip to content

Commit c8a79ca

Browse files
hpmy-devhpmy-dev
authored andcommitted
改善1
1 parent d276676 commit c8a79ca

4 files changed

Lines changed: 339 additions & 18 deletions

File tree

sakura_core/agent/CGrepAgent.cpp

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
#include <set>
4444
#include <functional>
4545
#include <string_view>
46+
#ifdef _DEBUG
47+
#include <chrono>
48+
#endif
4649

4750
constexpr DWORD UICHECK_INTERVAL_MILLISEC = 100; // UI確認の時間間隔
4851
constexpr DWORD ADDTAIL_INTERVAL_MILLISEC = 50; // 結果出力の時間間隔
@@ -175,6 +178,9 @@ class CFileLoadOrWnd{
175178
}
176179
int GetPercent(){
177180
if( m_hWnd ){
181+
if( m_nLineNum <= 0 ){
182+
return 0; // 行数 0 のウインドウでのゼロ除算を防ぐ
183+
}
178184
return (int)(m_nLineCurrent * 100.0 / m_nLineNum);
179185
}
180186
return m_cfl.GetPercent();
@@ -286,9 +292,12 @@ static int GetHwndTitle(HWND& hWndTarget, CNativeW* pmemTitle, WCHAR* pszWindowN
286292
if( 0 != wcsncmp(pszFile, szTargetPrefix, cchTargetPrefix) ){
287293
return 0; // ハンドルGrepではない
288294
}
289-
if( 0 >= ::swscanf_s(pszFile + cchTargetPrefix, L"%zx", (size_t*)&hWndTarget) || !IsSakuraMainWindow(hWndTarget) ){
295+
// HWND* を size_t* として読み書きする型パンニングを避けるため、一旦 size_t で受けてから変換する
296+
size_t nHwndValue = 0;
297+
if( 0 >= ::swscanf_s(pszFile + cchTargetPrefix, L"%zx", &nHwndValue) || !IsSakuraMainWindow((HWND)nHwndValue) ){
290298
return -1; // ハンドルを読み取れなかった、または、対象ウインドウハンドルが存在しない
291299
}
300+
hWndTarget = (HWND)nHwndValue;
292301
if( pmemTitle ){
293302
const wchar_t* p = L"Window:[";
294303
pmemTitle->SetStringHoldBuffer(p, 8);
@@ -301,6 +310,9 @@ static int GetHwndTitle(HWND& hWndTarget, CNativeW* pmemTitle, WCHAR* pszWindowN
301310
WCHAR szGrep[100];
302311
editInfo->m_bIsModified = false;
303312
const EditNode* node = CAppNodeManager::getInstance()->GetEditNode(hWndTarget);
313+
if( !node ){
314+
return -1; // 管理リストに見つからない(対象ウインドウが閉じられた直後など)
315+
}
304316
WCHAR* pszTagName = szTitle;
305317
if( editInfo->m_bIsGrep ){
306318
// Grepは検索キーとタグがぶつかることがあるので単に(Grep)と表示
@@ -409,7 +421,8 @@ DWORD CGrepAgent::DoGrep(
409421
}
410422
if( bLineSelect ){
411423
int len = cmemReplace.GetStringLength();
412-
if( cmemReplace[len - 1] != WCODE::CR && cmemReplace[len - 1] != WCODE::LF ){
424+
// 空クリップボード時に cmemReplace[len - 1] が範囲外参照になるのを防ぐ
425+
if( len == 0 || (cmemReplace[len - 1] != WCODE::CR && cmemReplace[len - 1] != WCODE::LF) ){
413426
cmemReplace.AppendString(pcViewDst->GetDocument()->m_cDocEditor.GetNewLineCode().GetValue2());
414427
}
415428
}
@@ -2612,6 +2625,34 @@ CNativeW CGrepAgent::BuildGrepFooter(int nHitCount, bool bGrepReplace)
26122625
return cmemMessage;
26132626
}
26142627

2628+
#ifdef _DEBUG
2629+
// 0-5: 性能計測基盤(Debug 限定)
2630+
// 並列 Grep の 列挙時間 / 検索時間 / resultMutex ロック待ち / バッチ終端待ち を集計し、
2631+
// Grep 終了時に MYTRACE で出力する。Release ビルドでは一切のコードを生成しない。
2632+
struct SGrepPerfStats {
2633+
std::atomic<long long> enumUs{ 0 }; // ファイル列挙時間(メインスレッド、μ秒)
2634+
std::atomic<long long> searchUs{ 0 }; // ファイル内検索時間(全ワーカー合算、μ秒)
2635+
std::atomic<long long> lockWaitUs{ 0 }; // resultMutex ロック待ち(全ワーカー合算、μ秒)
2636+
std::atomic<long long> batchWaitUs{ 0 }; // バッチ終端待ち(メインスレッド、μ秒)
2637+
void Trace() const {
2638+
MYTRACE( L"[GrepPerf] enum=%lldus search=%lldus lockWait=%lldus batchWait=%lldus\n",
2639+
enumUs.load(), searchUs.load(), lockWaitUs.load(), batchWaitUs.load() );
2640+
}
2641+
};
2642+
static long long GrepPerfNowUs()
2643+
{
2644+
using namespace std::chrono;
2645+
return duration_cast<microseconds>( steady_clock::now().time_since_epoch() ).count();
2646+
}
2647+
#define GREP_PERF_BEGIN( var ) const long long var = GrepPerfNowUs()
2648+
#define GREP_PERF_ADD( field, var ) perfStats.field.fetch_add( GrepPerfNowUs() - (var) )
2649+
#define GREP_PERF_TRACE() perfStats.Trace()
2650+
#else
2651+
#define GREP_PERF_BEGIN( var ) ((void)0)
2652+
#define GREP_PERF_ADD( field, var ) ((void)0)
2653+
#define GREP_PERF_TRACE() ((void)0)
2654+
#endif
2655+
26152656
/*! @brief 並列Grep: スレッドプールでファイル検索を並列化する
26162657
@retval -1 キャンセル
26172658
@retval 0 完了
@@ -2647,6 +2688,11 @@ int CGrepAgent::RunParallelGrep(
26472688
// ヒット数(全バッチ共通・バッチ間で引き継ぐ)
26482689
std::atomic<int> atomicHitCount{ 0 };
26492690

2691+
#ifdef _DEBUG
2692+
// 0-5: 性能計測(Debug 限定)
2693+
SGrepPerfStats perfStats;
2694+
#endif
2695+
26502696
// ===== スレッドプール: バッチ間でスレッドを再利用し生成・破棄コストを排除 =====
26512697
// バッチ番号インクリメントで新バッチを通知し、condition_variable で待機中ワーカーを起床させる。
26522698
std::mutex poolMutex;
@@ -2772,20 +2818,24 @@ int CGrepAgent::RunParallelGrep(
27722818
// ファイル内検索
27732819
localMessage._SetStringLength(0);
27742820
const SGrepSearchParams workerParams{ searchKey.c_str(), sSearchOption, sGrepOption };
2821+
GREP_PERF_BEGIN( perfSearchStart );
27752822
const int fileHits = DoGrepFileWorker(
27762823
workerParams, task,
27772824
&localRegexp, localPattern,
27782825
bWorkCancelled,
27792826
localMessage, localUnicodeBuffer
27802827
);
2828+
GREP_PERF_ADD( searchUs, perfSearchStart );
27812829

27822830
if( fileHits == -1 ){
27832831
bWorkCancelled.store( true );
27842832
break;
27852833
}
27862834

27872835
if( fileHits > 0 || localMessage.GetStringLength() > 0 ){
2836+
GREP_PERF_BEGIN( perfLockStart );
27882837
std::scoped_lock lk( resultMutex );
2838+
GREP_PERF_ADD( lockWaitUs, perfLockStart );
27892839
// フォルダーヘッダー重複排除(最初のマッチ時のみ出力)
27902840
if( sGrepOption.bGrepOutputBaseFolder &&
27912841
!writtenBaseFolders.contains(task.baseFolder) ){
@@ -2826,6 +2876,14 @@ int CGrepAgent::RunParallelGrep(
28262876
cvInitDone.wait( lk, [&]{ return nInitDone.load() >= nThreads; } );
28272877
}
28282878

2879+
// 全ワーカーが初期化に失敗した場合、結果 0 件のまま無警告で終了せずエラーとして中断する
2880+
if( nActiveWorkers.load() == 0 ){
2881+
ErrorMessage( pcViewDst != nullptr ? pcViewDst->m_hwndParent : nullptr,
2882+
L"Grep: 検索用ワーカースレッドの初期化にすべて失敗しました。検索を中断します。" ); // TODO: Phase 5 E-1 で LS() 化
2883+
nHitCountOut = 0;
2884+
return -1;
2885+
}
2886+
28292887
// バッチ実行ラムダ: vecTasks をプールのワーカーに割り当て結果を出力する
28302888
// 戻り値: true=継続, false=キャンセル発生
28312889
auto RunBatch = [&]( const std::vector<SGrepFileTask>& vecTasks ) {
@@ -2843,6 +2901,7 @@ int CGrepAgent::RunParallelGrep(
28432901
cvPoolStart.notify_all();
28442902

28452903
// メインスレッド: 全ワーカーが完全終了するまでUI更新・キャンセル監視・結果フラッシュを継続
2904+
GREP_PERF_BEGIN( perfBatchStart );
28462905
while( poolBatchActive.load() > 0 ){
28472906
::Sleep(5);
28482907

@@ -2875,6 +2934,7 @@ int CGrepAgent::RunParallelGrep(
28752934
cmemMessage._SetStringLength(0);
28762935
}
28772936
}
2937+
GREP_PERF_ADD( batchWaitUs, perfBatchStart );
28782938

28792939
// 最終フラッシュ
28802940
{
@@ -2909,12 +2969,14 @@ int CGrepAgent::RunParallelGrep(
29092969
sGrepOptionNoSub.bGrepSubFolder = false;
29102970
bool bEnumCancelled = false;
29112971
std::vector<SGrepFileTask> vecTasks;
2972+
GREP_PERF_BEGIN( perfEnumStart );
29122973
DoGrepTreeEnumerate(
29132974
pcDlgCancel, sGrepOptionNoSub,
29142975
SGrepEnumContext{ cGrepEnumKeys, cGrepExceptAbsFiles, cGrepExceptAbsFolders },
29152976
sPath.c_str(), sPath.c_str(),
29162977
vecTasks, bEnumCancelled
29172978
);
2979+
GREP_PERF_ADD( enumUs, perfEnumStart );
29182980
if( bEnumCancelled ){ nGrepTreeResult = -1; break; }
29192981
if( !RunBatch(vecTasks) ){ nGrepTreeResult = -1; break; }
29202982
}
@@ -2925,20 +2987,24 @@ int CGrepAgent::RunParallelGrep(
29252987
}
29262988
CGrepEnumOptions cGrepEnumOptionsDir;
29272989
CGrepEnumFilterFolders cTopFolders;
2990+
GREP_PERF_BEGIN( perfEnumDirStart );
29282991
cTopFolders.Enumerates(
29292992
sPath.c_str(), cGrepEnumKeys, cGrepEnumOptionsDir, cGrepExceptAbsFolders );
2993+
GREP_PERF_ADD( enumUs, perfEnumDirStart );
29302994

29312995
const int nFolderCount = cTopFolders.GetCount();
29322996
for( int fi = 0; fi < nFolderCount && nGrepTreeResult != -1; fi++ ){
29332997
std::wstring subFolder = sPath + L"\\" + cTopFolders.GetFileName(fi);
29342998
bool bEnumCancelled = false;
29352999
std::vector<SGrepFileTask> vecTasks;
3000+
GREP_PERF_BEGIN( perfEnumSubStart );
29363001
DoGrepTreeEnumerate(
29373002
pcDlgCancel, sGrepOption,
29383003
SGrepEnumContext{ cGrepEnumKeys, cGrepExceptAbsFiles, cGrepExceptAbsFolders },
29393004
subFolder.c_str(), sPath.c_str(),
29403005
vecTasks, bEnumCancelled
29413006
);
3007+
GREP_PERF_ADD( enumUs, perfEnumSubStart );
29423008
if( bEnumCancelled ){ nGrepTreeResult = -1; break; }
29433009
if( !RunBatch(vecTasks) ){ nGrepTreeResult = -1; break; }
29443010
}
@@ -2947,5 +3013,6 @@ int CGrepAgent::RunParallelGrep(
29473013
// スレッドプールのシャットダウンと join は PoolJoinGuard のデストラクタが
29483014
// 担う(正常終了・例外伝播のどちらの経路でも確実に実行される)。
29493015
nHitCountOut = atomicHitCount.load();
3016+
GREP_PERF_TRACE();
29503017
return nGrepTreeResult;
29513018
}

sakura_core/dlg/CDlgGrep.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -828,14 +828,6 @@ int CDlgGrep::GetData( void )
828828
/* 除外フォルダー */
829829
ApiWrap::DlgItem_GetText( GetHwnd(), IDC_COMBO_EXCLUDE_FOLDER, m_szExcludeFolder, std::size(m_szExcludeFolder));
830830

831-
m_pShareData->m_Common.m_sSearch.m_nGrepCharSet = m_nGrepCharSet; // 文字コード自動判別
832-
m_pShareData->m_Common.m_sSearch.m_nGrepOutputLineType = m_nGrepOutputLineType; // 行を出力/該当部分/否マッチ行 を出力
833-
m_pShareData->m_Common.m_sSearch.m_nGrepOutputStyle = m_nGrepOutputStyle; // Grep: 出力形式
834-
m_pShareData->m_Common.m_sSearch.m_bGrepOutputFileOnly = m_bGrepOutputFileOnly;
835-
m_pShareData->m_Common.m_sSearch.m_bGrepOutputBaseFolder = m_bGrepOutputBaseFolder;
836-
m_pShareData->m_Common.m_sSearch.m_bGrepSeparateFolder = m_bGrepSeparateFolder;
837-
m_pShareData->m_Common.m_sSearch.m_bGrepExcludeFileRegexp = m_bExcludeFileRegularExp != FALSE;
838-
839831
if( m_szFile[0] != '\0' ) {
840832
CGrepEnumKeys enumKeys;
841833
int nErrorNo = enumKeys.SetFileKeys( m_szFile );
@@ -911,6 +903,18 @@ int CDlgGrep::GetData( void )
911903
return FALSE;
912904
}
913905
// To Here Jun. 26, 2001 genta 正規表現ライブラリ差し替え
906+
}
907+
908+
// ==== ここから下では入力検証エラーで戻らない(共有設定への書き込みは全検証通過後に集約する) ====
909+
m_pShareData->m_Common.m_sSearch.m_nGrepCharSet = m_nGrepCharSet; // 文字コード自動判別
910+
m_pShareData->m_Common.m_sSearch.m_nGrepOutputLineType = m_nGrepOutputLineType; // 行を出力/該当部分/否マッチ行 を出力
911+
m_pShareData->m_Common.m_sSearch.m_nGrepOutputStyle = m_nGrepOutputStyle; // Grep: 出力形式
912+
m_pShareData->m_Common.m_sSearch.m_bGrepOutputFileOnly = m_bGrepOutputFileOnly;
913+
m_pShareData->m_Common.m_sSearch.m_bGrepOutputBaseFolder = m_bGrepOutputBaseFolder;
914+
m_pShareData->m_Common.m_sSearch.m_bGrepSeparateFolder = m_bGrepSeparateFolder;
915+
m_pShareData->m_Common.m_sSearch.m_bGrepExcludeFileRegexp = m_bExcludeFileRegularExp != FALSE;
916+
917+
if( m_strText.size() > 0 ){
914918
if( m_strText.size() < _MAX_PATH ){
915919
if( !m_bFromThisText ){
916920
CSearchKeywordManager().AddToSearchKeyArr( m_strText.c_str() );

sakura_core/dlg/CDlgGrepReplace.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,20 @@ int CDlgGrepReplace::DoModal( HINSTANCE hInstance, HWND hwndParent, const WCHAR*
8585
m_bBackup = m_pShareData->m_Common.m_sSearch.m_bGrepBackup;
8686

8787
if( m_szFile[0] == L'\0' && m_pShareData->m_sSearchKeywords.m_aGrepFiles.size() ){
88-
wcscpy( m_szFile, m_pShareData->m_sSearchKeywords.m_aGrepFiles[0] ); /* 検索ファイル */
88+
wcscpy_s( m_szFile, std::size(m_szFile), m_pShareData->m_sSearchKeywords.m_aGrepFiles[0] ); /* 検索ファイル */
8989
}
9090
if( m_szFolder[0] == L'\0' && m_pShareData->m_sSearchKeywords.m_aGrepFolders.size() ){
91-
wcscpy( m_szFolder, m_pShareData->m_sSearchKeywords.m_aGrepFolders[0] ); /* 検索フォルダー */
91+
wcscpy_s( m_szFolder, std::size(m_szFolder), m_pShareData->m_sSearchKeywords.m_aGrepFolders[0] ); /* 検索フォルダー */
9292
}
9393

9494
/* 除外ファイル */
9595
if (m_szExcludeFile[0] == L'\0') {
9696
if (m_pShareData->m_sSearchKeywords.m_aExcludeFiles.size()) {
97-
wcscpy(m_szExcludeFile, m_pShareData->m_sSearchKeywords.m_aExcludeFiles[0]);
97+
wcscpy_s(m_szExcludeFile, std::size(m_szExcludeFile), m_pShareData->m_sSearchKeywords.m_aExcludeFiles[0]);
9898
}
9999
else {
100100
/* ユーザーの利便性向上のために除外ファイルに対して初期値を設定する */
101-
wcscpy(m_szExcludeFile, DEFAULT_EXCLUDE_FILE_PATTERN_REGEX); /* 除外ファイル */
101+
wcscpy_s(m_szExcludeFile, std::size(m_szExcludeFile), DEFAULT_EXCLUDE_FILE_PATTERN_REGEX); /* 除外ファイル */
102102

103103
/* 履歴に残して後で選択できるようにする */
104104
m_pShareData->m_sSearchKeywords.m_aExcludeFiles.push_back(DEFAULT_EXCLUDE_FILE_PATTERN_REGEX);
@@ -108,19 +108,19 @@ int CDlgGrepReplace::DoModal( HINSTANCE hInstance, HWND hwndParent, const WCHAR*
108108
/* 除外フォルダー */
109109
if (m_szExcludeFolder[0] == L'\0') {
110110
if (m_pShareData->m_sSearchKeywords.m_aExcludeFolders.size()) {
111-
wcscpy(m_szExcludeFolder, m_pShareData->m_sSearchKeywords.m_aExcludeFolders[0]);
111+
wcscpy_s(m_szExcludeFolder, std::size(m_szExcludeFolder), m_pShareData->m_sSearchKeywords.m_aExcludeFolders[0]);
112112
}
113113
else {
114114
/* ユーザーの利便性向上のために除外フォルダーに対して初期値を設定する */
115-
wcscpy(m_szExcludeFolder, DEFAULT_EXCLUDE_FOLDER_PATTERN); /* 除外フォルダー */
115+
wcscpy_s(m_szExcludeFolder, std::size(m_szExcludeFolder), DEFAULT_EXCLUDE_FOLDER_PATTERN); /* 除外フォルダー */
116116

117117
/* 履歴に残して後で選択できるようにする */
118118
m_pShareData->m_sSearchKeywords.m_aExcludeFolders.push_back(DEFAULT_EXCLUDE_FOLDER_PATTERN);
119119
}
120120
}
121121

122122
if( pszCurrentFilePath ){ // 2010.01.10 ryoji
123-
wcscpy(m_szCurrentFilePath, pszCurrentFilePath);
123+
wcscpy_s(m_szCurrentFilePath, std::size(m_szCurrentFilePath), pszCurrentFilePath);
124124
}
125125

126126
return (int)CDialog::DoModal( hInstance, hwndParent, IDD_GREP_REPLACE, lParam );
@@ -227,12 +227,14 @@ int CDlgGrepReplace::GetData( void )
227227
}
228228

229229
m_bBackup = IsDlgButtonCheckedBool( GetHwnd(), IDC_CHK_BACKUP );
230-
m_pShareData->m_Common.m_sSearch.m_bGrepBackup = m_bBackup;
231230

232231
if( !CDlgGrep::GetData() ){
233232
return FALSE;
234233
}
235234

235+
// 全検証通過後に共有設定へ書き込む(入力エラー時に共有設定を汚さない)
236+
m_pShareData->m_Common.m_sSearch.m_bGrepBackup = m_bBackup;
237+
236238
if( m_strText2.size() < _MAX_PATH ){
237239
CSearchKeywordManager().AddToReplaceKeyArr( m_strText2.c_str() );
238240
}

0 commit comments

Comments
 (0)