#if MEMMONITOR
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace TIASshot {
/// <summary>
/// メモリ観測ウィンドウ(MEMMONITOR 限定)。
/// TIASshot 本体プロセスのメモリ使用量をライブ折れ線グラフで表示する。
/// 研究室デモ用:実使用時にメモリが増え続けないことを画面録画で示す。
/// </summary>
public partial class MemoryMonitorForm : Form {
// ---- サンプリング記録 -------------------------------------------------
private struct MemSample {
public double ElapsedSec;
public double PrivateMB;
public double WorkingSetMB;
}
private readonly List<MemSample> _samples = new List<MemSample>();
private readonly System.Windows.Forms.Timer _timer;
private readonly Stopwatch _stopwatch = new Stopwatch();
private bool _isRunning;
// CSV 出力先
private readonly string _outputDir;
// ---- コンストラクタ ---------------------------------------------------
public MemoryMonitorForm() {
InitializeComponent();
// モードラベルとウィンドウタイトルをビルド種別に応じて設定
#if LEAKY_REPRO
lblMode.Text = "モード: リーク再現(修正前)";
lblMode.ForeColor = System.Drawing.Color.Red;
this.Text = "メモリ観測ウィンドウ [MEMMONITOR] — リーク再現";
#else
lblMode.Text = "モード: 修正後";
lblMode.ForeColor = System.Drawing.Color.Green;
this.Text = "メモリ観測ウィンドウ [MEMMONITOR] — 修正後";
#endif
_outputDir = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"MemMonitor_Results");
Directory.CreateDirectory(_outputDir);
_timer = new System.Windows.Forms.Timer();
_timer.Interval = 500;
_timer.Tick += Timer_Tick;
this.FormClosing += (s, e) => {
_timer.Stop();
};
// ウィンドウを開いたら自動でサンプリング開始
this.Shown += (s, e) => StartSampling();
}
// ---- サンプリング制御 ------------------------------------------------
private void StartSampling() {
if (_isRunning) return;
_isRunning = true;
_stopwatch.Start();
_timer.Start();
btnStartStop.Text = "停止";
}
private void StopSampling() {
if (!_isRunning) return;
_isRunning = false;
_stopwatch.Stop();
_timer.Stop();
btnStartStop.Text = "開始";
}
// ---- タイマー Tick(サンプリング本体)--------------------------------
private void Timer_Tick(object sender, EventArgs e) {
// Process.Refresh() で最新値を取得(GC 強制なし)
using (var proc = Process.GetCurrentProcess()) {
proc.Refresh();
double elapsedSec = _stopwatch.Elapsed.TotalSeconds;
double privateMB = proc.PrivateMemorySize64 / (1024.0 * 1024.0);
double wsMB = proc.WorkingSet64 / (1024.0 * 1024.0);
// 記録
_samples.Add(new MemSample {
ElapsedSec = elapsedSec,
PrivateMB = privateMB,
WorkingSetMB = wsMB,
});
// グラフ追加
double privateGB = privateMB / 1024.0;
double wsGB = wsMB / 1024.0;
chart.Series["Private"].Points.AddXY(elapsedSec, privateGB);
chart.Series["WorkingSet"].Points.AddXY(elapsedSec, wsGB);
// Y 軸オートスケール追従
chart.ChartAreas["ChartArea1"].RecalculateAxesScale();
// ラベル更新
int n = (int)Math.Round(elapsedSec);
lblCurrent.Text =
$"現在: Private {privateGB:F2} GB / WS {wsGB:F2} GB(経過 {n}s)";
}
}
// ---- ボタンイベント --------------------------------------------------
private void btnStartStop_Click(object sender, EventArgs e) {
if (_isRunning) {
StopSampling();
} else {
StartSampling();
}
}
private void btnReset_Click(object sender, EventArgs e) {
StopSampling();
_samples.Clear();
_stopwatch.Reset();
chart.Series["Private"].Points.Clear();
chart.Series["WorkingSet"].Points.Clear();
lblCurrent.Text = "現在: --- (リセット)";
}
private void btnSaveCsv_Click(object sender, EventArgs e) {
if (_samples.Count == 0) {
MessageBox.Show("記録データがありません。", "CSV 保存",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
string path = NextCsvPath();
var sb = new StringBuilder();
sb.AppendLine("elapsed_sec,private_MB,ws_MB");
foreach (var s in _samples) {
sb.AppendLine($"{s.ElapsedSec:F3},{s.PrivateMB:F2},{s.WorkingSetMB:F2}");
}
File.WriteAllText(path, sb.ToString(), new UTF8Encoding(false));
MessageBox.Show($"保存しました:\n{path}", "CSV 保存",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void btnOpenFolder_Click(object sender, EventArgs e) {
Directory.CreateDirectory(_outputDir);
Process.Start("explorer.exe", _outputDir);
}
// ---- ユーティリティ --------------------------------------------------
private string NextCsvPath() {
const string prefix = "mem_monitor_";
int max = 0;
if (Directory.Exists(_outputDir)) {
foreach (var f in Directory.GetFiles(_outputDir, prefix + "*.csv")) {
string name = Path.GetFileNameWithoutExtension(f);
string numPart = name.Substring(prefix.Length);
if (int.TryParse(numPart, out int n) && n > max) max = n;
}
}
return Path.Combine(_outputDir, $"{prefix}{max + 1:D4}.csv");
}
}
}
#endif