Last active
October 16, 2016 05:56
-
-
Save azyobuzin/db9954d47c83bc99e9ad37070ab84a18 to your computer and use it in GitHub Desktop.
ソートの様子を観察するやつ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.suo | |
*.user | |
*.userprefs | |
bin/ | |
obj/ | |
.vs/ | |
packages/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#define NUM 5 | |
int main(void) { | |
int test[NUM]; | |
int tmp; | |
int i, j, s, t; | |
printf("%d人の点数を入力してください。\n", NUM); | |
for(i=0; i<NUM; i++){ | |
scanf("%d", &test[i]); | |
} | |
for(s=0; s<NUM-1; s++){ | |
for(t=s+1; t<NUM; t++){ | |
if(test[t] > test[s]){ | |
tmp = test[t]; | |
test[t] = test[s]; | |
test[s] = tmp; | |
} | |
} | |
} | |
for(j=0; j<NUM; j++){ | |
printf("%d番目の人の点数は%dです。\n", j+1, test[j]); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
// Information about this assembly is defined by the following attributes. | |
// Change them to the values specific to your project. | |
[assembly: AssemblyTitle("DebugSample5")] | |
[assembly: AssemblyDescription("")] | |
[assembly: AssemblyConfiguration("")] | |
[assembly: AssemblyCompany("")] | |
[assembly: AssemblyProduct("")] | |
[assembly: AssemblyCopyright("")] | |
[assembly: AssemblyTrademark("")] | |
[assembly: AssemblyCulture("")] | |
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". | |
// The form "{Major}.{Minor}.*" will automatically update the build and revision, | |
// and "{Major}.{Minor}.{Build}.*" will update just the revision. | |
[assembly: AssemblyVersion("1.0.*")] | |
// The following attributes are used to specify the signing key for the assembly, | |
// if desired. See the Mono documentation for more information about signing. | |
//[assembly: AssemblyDelaySign(false)] | |
//[assembly: AssemblyKeyFile("")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
<PropertyGroup> | |
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |
<Platform Condition=" '$(Platform)' == '' ">x86</Platform> | |
<ProjectGuid>{D72D4744-EF0E-41A5-A5C7-1D08C96798A8}</ProjectGuid> | |
<OutputType>Exe</OutputType> | |
<RootNamespace>DebugSample5</RootNamespace> | |
<AssemblyName>DebugSample5</AssemblyName> | |
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | |
</PropertyGroup> | |
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> | |
<DebugSymbols>true</DebugSymbols> | |
<DebugType>full</DebugType> | |
<Optimize>false</Optimize> | |
<OutputPath>bin\Debug</OutputPath> | |
<DefineConstants>DEBUG;</DefineConstants> | |
<ErrorReport>prompt</ErrorReport> | |
<WarningLevel>4</WarningLevel> | |
<ExternalConsole>true</ExternalConsole> | |
<PlatformTarget>x86</PlatformTarget> | |
</PropertyGroup> | |
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> | |
<Optimize>true</Optimize> | |
<OutputPath>bin\Release</OutputPath> | |
<ErrorReport>prompt</ErrorReport> | |
<WarningLevel>4</WarningLevel> | |
<ExternalConsole>true</ExternalConsole> | |
<PlatformTarget>x86</PlatformTarget> | |
</PropertyGroup> | |
<ItemGroup> | |
<Reference Include="System" /> | |
<Reference Include="Parseq"> | |
<HintPath>packages\Parseq.3.0.0.0\lib\portable-net40+sl5+win8+wp8+wpa81\Parseq.dll</HintPath> | |
</Reference> | |
<Reference Include="Microsoft.CSharp" /> | |
<Reference Include="System.Core" /> | |
<Reference Include="System.Drawing" /> | |
</ItemGroup> | |
<ItemGroup> | |
<Compile Include="GdbOutputParser.cs" /> | |
<Compile Include="GdbSession.cs" /> | |
<Compile Include="Program.cs" /> | |
<Compile Include="GdbResultRecord.cs" /> | |
<Compile Include="GdbAsyncRecord.cs" /> | |
<Compile Include="Utils.cs" /> | |
<Compile Include="AssemblyInfo.cs" /> | |
</ItemGroup> | |
<ItemGroup> | |
<None Include="packages.config" /> | |
</ItemGroup> | |
<ItemGroup> | |
<Content Include="7_Sample5.c"> | |
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
</Content> | |
</ItemGroup> | |
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Microsoft Visual Studio Solution File, Format Version 12.00 | |
# Visual Studio 2012 | |
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugSample5", "DebugSample5.csproj", "{D72D4744-EF0E-41A5-A5C7-1D08C96798A8}" | |
EndProject | |
Global | |
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |
Debug|x86 = Debug|x86 | |
Release|x86 = Release|x86 | |
EndGlobalSection | |
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |
{D72D4744-EF0E-41A5-A5C7-1D08C96798A8}.Debug|x86.ActiveCfg = Debug|x86 | |
{D72D4744-EF0E-41A5-A5C7-1D08C96798A8}.Debug|x86.Build.0 = Debug|x86 | |
{D72D4744-EF0E-41A5-A5C7-1D08C96798A8}.Release|x86.ActiveCfg = Release|x86 | |
{D72D4744-EF0E-41A5-A5C7-1D08C96798A8}.Release|x86.Build.0 = Release|x86 | |
EndGlobalSection | |
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace DebugSample5 | |
{ | |
public class GdbAsyncRecord | |
{ | |
public int? Token { get; set; } | |
public AsyncRecordType Type { get; set; } | |
public AsyncClass Class { get; set; } | |
public dynamic Result { get; set; } | |
} | |
public enum AsyncClass | |
{ | |
Stopped | |
} | |
public enum AsyncRecordType | |
{ | |
Exec, | |
Status, | |
Notify | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Dynamic; | |
using System.Linq; | |
using System.Text.RegularExpressions; | |
using Parseq; | |
using Parseq.Combinators; | |
namespace DebugSample5 | |
{ | |
public static class GdbOutputParser | |
{ | |
// https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Output-Syntax.html | |
private static readonly Parser<char, GdbResultRecord> s_resultRecord; | |
private static readonly Parser<char, GdbAsyncRecord> s_asyncRecord; | |
static GdbOutputParser() | |
{ | |
var token = Chars.Char(c => c >= '0' && c <= '9').Many1() | |
.Select(x => int.Parse(string.Concat(x))); | |
var variable = Chars.Char(c => (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '-') | |
.Many1().Select(x => string.Concat(x)); | |
var cstring = Chars.Sequence("\\\"").Or(Chars.Char(c => c != '"').Select(c => (IEnumerable<char>)new[] { c })) | |
.Many0() | |
.Between(Chars.Char('"').Ignore(), Chars.Char('"').Ignore()) | |
.Select(x => Regex.Unescape(string.Concat(x.Select(y => string.Concat(y))))); | |
var className = Chars.Char(c => c >= 'a' && c <= 'z').Many1(); | |
var resultClass = className.Select(ParseEnum<ResultClass>); | |
var asyncClass = className.Select(ParseEnum<AsyncClass>); | |
Parser<char, KeyValuePair<string, object>> _result = null; | |
var result = Combinator.Lazy(() => _result); | |
Parser<char, object> _value = null; | |
var value = Combinator.Lazy(() => _value); | |
var tuple = Combinator.SepBy0(result, Chars.Char(',').Ignore()) | |
.Between(Chars.OneOf('{', '[').Ignore(), Chars.OneOf('}', ']').Ignore()) | |
.Select(CreateExpandoObject); | |
var list = Combinator.SepBy0(value, Chars.Char(',').Ignore()) | |
.Between(Chars.Char('[').Ignore(), Chars.Char(']').Ignore()) | |
.Select(Enumerable.ToArray); | |
_value = Combinator.Choice( | |
cstring.Select(x => (object)x), | |
tuple, | |
list.Select(x => (object)x), | |
Parser.FailFast<char, object>("const | tuple | list を満たしません。") | |
); | |
_result = variable.Pipe(Chars.Char('='), _value, (k, _, v) => new KeyValuePair<string, object>(k, v)); | |
s_resultRecord = token.Optional().Pipe( | |
Chars.Char('^'), | |
resultClass, | |
Chars.Char(',').Bindr(_result).Many0(), | |
(t, _, c, r) => new GdbResultRecord() | |
{ | |
Token = t.Case(() => (int?)null, x => x), | |
Class = c, | |
Result = CreateExpandoObject(r) | |
}); | |
var asyncOutput = asyncClass.Pipe(Chars.Char(',').Bindr(_result).Many0(), Tuple.Create); | |
s_asyncRecord = token.Optional().Pipe( | |
Combinator.Choice( | |
Chars.Char('*').Select(_ => AsyncRecordType.Exec), | |
Chars.Char('+').Select(_ => AsyncRecordType.Status), | |
Chars.Char('=').Select(_ => AsyncRecordType.Notify) | |
), | |
asyncOutput, | |
(t, a, o) => new GdbAsyncRecord() | |
{ | |
Token = t.Case(() => (int?)null, x => x), | |
Type = a, | |
Class = o.Item1, | |
Result = CreateExpandoObject(o.Item2) | |
}); | |
} | |
private static T ParseEnum<T>(IEnumerable<char> input) | |
{ | |
return (T)Enum.Parse(typeof(T), string.Concat(input), true); | |
} | |
private static object CreateExpandoObject(IEnumerable<KeyValuePair<string, object>> results) | |
{ | |
var eo = (IDictionary<string, object>)new ExpandoObject(); | |
foreach (var x in results) eo.Add(x.Key, x.Value); | |
return eo; | |
} | |
private static T Parse<T>(Parser<char, T> parser, string input) | |
{ | |
return parser.Run(input.AsStream()).Case( | |
(stream, msg) => | |
{ | |
var exMsg = stream.Current.Select(x => x.Item1).Case(() => msg, pos => $"{msg}\n{pos.Line}:{pos.Column}"); | |
throw new FormatException(exMsg); | |
}, | |
(stream, result) => result | |
); | |
} | |
public static GdbResultRecord ParseResultRecord(string input) => Parse(s_resultRecord, input); | |
public static GdbAsyncRecord ParseAsyncRecord(string input) => Parse(s_asyncRecord, input); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
namespace DebugSample5 | |
{ | |
public class GdbResultRecord | |
{ | |
public int? Token { get; set; } | |
public ResultClass Class { get; set; } | |
public dynamic Result { get; set; } | |
public GdbResultRecord Expect(ResultClass resultClass) | |
{ | |
if (this.Class != resultClass) | |
{ | |
object msg; | |
throw new InvalidOperationException( | |
((IDictionary<string, object>)this.Result).TryGetValue("msg", out msg) | |
? msg.ToString() | |
: $"Expected: {resultClass}, Actual: {this.Class}" | |
); | |
} | |
return this; | |
} | |
} | |
public enum ResultClass | |
{ | |
None, | |
Done, | |
Running, | |
Connected, | |
Error, | |
Exit | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// テスト用 | |
//#define ECHO | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
namespace DebugSample5 | |
{ | |
public class GdbSession : IDisposable | |
{ | |
private readonly Process _process; | |
private readonly StreamWriter _stdin; | |
private readonly StreamReader _stdout; | |
private int _token = 0; | |
private GdbSession(Process process) | |
{ | |
_process = process; | |
_stdin = process.StandardInput; | |
_stdout = process.StandardOutput; | |
} | |
/// <summary>GDB/MI プロセスを起動</summary> | |
public static GdbSession Start() | |
{ | |
var process = Process.Start( | |
new ProcessStartInfo("gdb", "--interpreter=mi") | |
{ | |
UseShellExecute = false, | |
RedirectStandardInput = true, | |
RedirectStandardOutput = true | |
} | |
); | |
return new GdbSession(process); | |
} | |
public void SendCommand(string command) | |
{ | |
#if ECHO | |
Console.WriteLine(command); | |
#endif | |
_stdin.WriteLine(command); | |
} | |
public IReadOnlyList<string> ReadRecords() | |
{ | |
// 「(gdb)」まで読み取る | |
var records = new List<string>(); | |
while (true) | |
{ | |
var line = _stdout.ReadLine(); | |
#if ECHO | |
Console.WriteLine(line); | |
#endif | |
if (line == null || line.StartsWith("(gdb)", StringComparison.Ordinal)) | |
return records; | |
records.Add(line); | |
} | |
} | |
/// <summary>コマンドを送信して、結果を読み取る</summary> | |
public GdbResultRecord SendAndRead(string command) | |
{ | |
var token = _token++; | |
// トークンを付けてコマンドを実行することで、戻り値を識別する | |
this.SendCommand($"{token:D}{command}"); | |
var expected = $"{token:D}^"; | |
foreach (var record in this.ReadRecords()) | |
{ | |
// それっぽいのがあればパースして返す | |
if (record.StartsWith(expected, StringComparison.Ordinal)) | |
{ | |
return GdbOutputParser.ParseResultRecord(record); | |
} | |
} | |
throw new InvalidOperationException("応答が返ってきませんでした。"); | |
} | |
/// <summary>プロセスにアタッチ</summary> | |
public void AttachProcessOrThreadGroup(int id) | |
{ | |
this.SendAndRead($"-target-attach {id:D}").Expect(ResultClass.Done); | |
} | |
/// <summary>シンボルファイルを読み込む</summary> | |
public void SymbolFile(string file) | |
{ | |
this.SendAndRead("-file-symbol-file " + ToCString(file)).Expect(ResultClass.Done); | |
} | |
/// <summary>シンボル情報付き実行可能ファイルを読み込む</summary> | |
public void ExecAndSymbolsFile(string file) | |
{ | |
this.SendAndRead("-file-exec-and-symbols " + ToCString(file)).Expect(ResultClass.Done); | |
} | |
/// <summary>ブレークポイントを設定</summary> | |
public int InsertBreak(string location) | |
{ | |
var result = this.SendAndRead("-break-insert " + ToCString(location)) | |
.Expect(ResultClass.Done) | |
.Result; | |
return Utils.ParseIntInvariant((string)result.bkpt.number); | |
} | |
/// <summary>実行再開</summary> | |
public void Continue() | |
{ | |
this.SendAndRead("-exec-continue").Expect(ResultClass.Running); | |
} | |
/// <summary>最初から実行</summary> | |
public void Run() | |
{ | |
this.SendAndRead("-exec-run").Expect(ResultClass.Running); | |
} | |
/// <summary>実行が停止した行を取得</summary> | |
public int ReadStoppedLine() | |
{ | |
foreach (var record in this.ReadRecords()) | |
{ | |
if (record.StartsWith("*stopped", StringComparison.Ordinal)) | |
{ | |
var asyncRecord = GdbOutputParser.ParseAsyncRecord(record); | |
return Utils.ParseIntInvariant((string)asyncRecord.Result.frame.line); | |
} | |
} | |
throw new InvalidOperationException("停止通知を読み取れませんでした。"); | |
} | |
/// <summary>次の命令まで実行</summary> | |
public void Next() | |
{ | |
this.SendAndRead("-exec-next").Expect(ResultClass.Running); | |
} | |
/// <summary>ローカル変数を取得</summary> | |
public IReadOnlyList<KeyValuePair<string, string>> GetLocals() | |
{ | |
var result = this.SendAndRead("-stack-list-locals 1") | |
.Expect(ResultClass.Done) | |
.Result; | |
var locals = (IEnumerable<dynamic>)result.locals; | |
return locals.Select(x => new KeyValuePair<string, string>((string)x.name, (string)x.value)).ToArray(); | |
} | |
public void Dispose(bool disposing) | |
{ | |
if (!_process.HasExited) | |
{ | |
// gdb プロセスを終了させる | |
this.SendCommand("-gdb-exit"); | |
_stdin.Close(); | |
_stdout.Close(); | |
if (!_process.WaitForExit(5000)) | |
_process.Kill(); | |
_process.Close(); | |
} | |
} | |
public void Dispose() | |
{ | |
this.Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
~GdbSession() | |
{ | |
this.Dispose(false); | |
} | |
private static string ToCString(string str) | |
{ | |
var sb = new StringBuilder(str) | |
.Replace("\\", "\\\\") | |
.Replace("\a", "\\a") | |
.Replace("\b", "\\b") | |
.Replace("\f", "\\f") | |
.Replace("\n", "\\n") | |
.Replace("\r", "\\r") | |
.Replace("\t", "\\t") | |
.Replace("\v", "\\v") | |
.Replace("\"", "\\\""); | |
return "\"" + sb + "\""; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<packages> | |
<package id="Parseq" version="3.0.0.0" targetFramework="net45" /> | |
</packages> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Drawing; | |
using System.IO; | |
using System.Linq; | |
namespace DebugSample5 | |
{ | |
public static class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
Console.WriteLine("コンパイル中"); | |
var cr = Compile(); | |
Console.WriteLine("実行ファイル: " + cr.ExecutableFile); | |
var snapshots = Debug(cr); | |
// 実行ファイルの一時ディレクトリを削除 | |
Directory.GetParent(cr.ExecutableFile).Delete(true); | |
foreach (var s in snapshots) | |
{ | |
Console.WriteLine($"{s.Line} 行目"); | |
foreach (var kvp in s.Locals) | |
Console.WriteLine($"{kvp.Key}: {kvp.Value}"); | |
Console.WriteLine(); | |
} | |
Console.WriteLine("画像出力中"); | |
CreateGifImage(snapshots); | |
} | |
private struct CompilationResult | |
{ | |
public string SourceFile { get; set; } | |
public string ExecutableFile { get; set; } | |
} | |
/// <summary>gcc でサンプルプログラムをコンパイルする</summary> | |
private static CompilationResult Compile() | |
{ | |
// ソースコードは、このアセンブリと同じディレクトリにコピーされる | |
var sourceFile = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "7_Sample5.c"); | |
// 作業ディレクトリを確保 | |
var workdir = Directory.CreateDirectory( | |
Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"))); | |
// gcc 実行 | |
using (var gcc = Process.Start(new ProcessStartInfo("gcc", $"-std=c11 -g \"{sourceFile}\"") | |
{ | |
WorkingDirectory = workdir.FullName | |
})) | |
{ | |
gcc.WaitForExit(); | |
if (gcc.ExitCode != 0) | |
throw new InvalidOperationException("コンパイル失敗"); | |
} | |
return new CompilationResult() | |
{ | |
SourceFile = sourceFile, | |
ExecutableFile = Path.Combine(workdir.FullName, "a.out") | |
}; | |
} | |
private struct LocalsSnapshot | |
{ | |
public int Line { get; set; } | |
public IReadOnlyDictionary<string, string> Locals { get; set; } | |
} | |
/// <summary>デバッグ実行をし、ループ中の変数の状態を取得</summary> | |
private static IReadOnlyList<LocalsSnapshot> Debug(CompilationResult cr) | |
{ | |
// gdb とコンパイルされた実行ファイルを起動 | |
// -file-exec-and-symbols を使うと、デバッグ対象の標準入出力までリダイレクトされていまうので | |
// 自分でプロセスを立ち上げることに(root権限が必要) | |
using (var gdb = GdbSession.Start()) | |
using (var targetProcess = Process.Start(cr.ExecutableFile)) | |
{ | |
gdb.ReadRecords(); // 最初の情報は無視 | |
// シンボル読み込み | |
gdb.SymbolFile(cr.ExecutableFile); | |
// アタッチ | |
gdb.AttachProcessOrThreadGroup(targetProcess.Id); | |
// 14行目にブレークポイント | |
gdb.InsertBreak($"{cr.SourceFile}:14"); | |
// 14行目まで実行 | |
gdb.Continue(); | |
var result = new List<LocalsSnapshot>(); | |
while (true) | |
{ | |
var line = gdb.ReadStoppedLine(); | |
// 止まった行と変数の状態を記録 | |
result.Add(new LocalsSnapshot() | |
{ | |
Line = line, | |
Locals = gdb.GetLocals().ToDictionary(x => x.Key, x => x.Value) | |
}); | |
// 23 行目以降なら、ループを越えたのでデバッグ終了 | |
if (line >= 23) break; | |
gdb.Next(); // 次の行へ | |
} | |
return result; | |
} | |
} | |
/// <summary>変数の変化の状況をアニメーション GIF として保存</summary> | |
private static void CreateGifImage(IReadOnlyList<LocalsSnapshot> snapshots) | |
{ | |
// Mono の System.Drawing の GIF はよろしくなさそうなので、 PNG 出力からの ImageMagick | |
// 大量の PNG を吐き出したいのでディレクトリを作る | |
var tmpDirectory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"))); | |
try | |
{ | |
for (var i = 0; i < snapshots.Count; i++) | |
{ | |
using (var img = CreateImage( | |
i > 0 ? snapshots[i - 1] : (LocalsSnapshot?)null, | |
snapshots[i], | |
i + 1, | |
snapshots.Count | |
)) | |
{ | |
img.Save(Path.Combine(tmpDirectory.FullName, $"{i:D4}.png")); | |
} | |
} | |
// ImageMagick に食わせる | |
using (var convert = Process.Start("convert", $"-delay 50 -loop 0 \"{tmpDirectory.FullName}/*.png\" result.gif")) | |
convert.WaitForExit(); | |
} | |
finally | |
{ | |
tmpDirectory.Delete(true); // 何があっても消す | |
} | |
} | |
private static readonly FontFamily s_fontFamily = FontFamily.Families | |
.FirstOrDefault(x => x.Name == "DejaVu Sans Mono") ?? FontFamily.GenericMonospace; | |
/// <summary>1フレームを作成</summary> | |
/// <param name="prev">1つ前のスナップショット(比較するため)</param> | |
/// <param name="current">このフレームに使うスナップショット</param> | |
private static Image CreateImage(LocalsSnapshot? prev, LocalsSnapshot current, int currentIndex, int total) | |
{ | |
const int num = 5; // Sample5.c の NUM | |
const int marginTop = 25; // 行数表示用に空けておく高さ | |
const int marginBottom = 5; // 枠と矢印の間 | |
const int width = 60; // 枠1つあたりの幅 | |
const int height = 30; // 枠1つあたりの高さ | |
var bmp = new Bitmap(width * (num + 1), 90); | |
using (var g = Graphics.FromImage(bmp)) | |
using (var stFont = new Font(s_fontFamily, 12)) | |
{ | |
// 背景塗りつぶし | |
g.FillRectangle(Brushes.White, new Rectangle(new Point(0, 0), bmp.Size)); | |
g.DrawString($"[{currentIndex}/{total}] Line {current.Line}", stFont, Brushes.Black, 0, 0); | |
// 枠線を描く | |
for (var i = 0; i < num; i++) | |
g.DrawRectangle(Pens.Silver, width * i, marginTop, width, height); | |
var prevTest = prev.HasValue ? ParseGdbIntArray(prev.Value.Locals["test"]) : null; | |
var currentTest = ParseGdbIntArray(current.Locals["test"]); | |
// test の中身を描く | |
using (var testFont = new Font(s_fontFamily, 20)) | |
{ | |
for (var i = 0; i < num; i++) | |
{ | |
var isChanged = prevTest != null && prevTest[i] != currentTest[i]; | |
var strToDraw = currentTest[i].ToString(); | |
var strSize = g.MeasureString(strToDraw, testFont); | |
g.DrawString( | |
strToDraw, testFont, | |
isChanged ? Brushes.Red : Brushes.Black, // 変更があったら赤にする | |
width * i + (width - strSize.Width) / 2.0f, | |
marginTop + (height - strSize.Height) / 2.0f | |
); | |
} | |
} | |
// s | |
using (var sPen = new Pen(Color.Blue, 2)) | |
{ | |
var s = Utils.ParseIntInvariant(current.Locals["s"]); | |
DrawArrowAndText(g, stFont, sPen, new PointF(width * s + width / 2.0f, marginTop + height + marginBottom), $"s={s}"); | |
} | |
// t | |
using (var tPen = new Pen(Color.Green, 2)) | |
{ | |
var t = Utils.ParseIntInvariant(current.Locals["t"]); | |
DrawArrowAndText(g, stFont, tPen, new PointF(width * t + width / 2.0f, marginTop + height + marginBottom), $"t={t}"); | |
} | |
} | |
return bmp; | |
} | |
/// <summary>{1, 2, 3, ...} 形式の表現から配列を作成</summary> | |
private static int[] ParseGdbIntArray(string s) | |
{ | |
return Array.ConvertAll( | |
s.Substring(1, s.Length - 2) // { と } を取り除く | |
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries), | |
Utils.ParseIntInvariant | |
); | |
} | |
/// <summary>上向き矢印を描き、その下に文字を描く</summary> | |
private static void DrawArrowAndText(Graphics g, Font font, Pen pen, PointF center, string str) | |
{ | |
const float w = 6; // 矢印の幅の半分 | |
const float h = 6; // 矢印の高さの差 | |
const float len = 12; // 棒の長さ | |
// 左側 | |
g.DrawLine(pen, center.X, center.Y, center.X - w, center.Y + h); | |
// 右側 | |
g.DrawLine(pen, center.X, center.Y, center.X + w, center.Y + h); | |
// 棒 | |
g.DrawLine(pen, center.X, center.Y, center.X, center.Y + len); | |
// 文字 | |
var strWidth = g.MeasureString(str, font).Width; | |
g.DrawString(str, font, pen.Brush, center.X - strWidth / 2.0f, center.Y + len); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Globalization; | |
namespace DebugSample5 | |
{ | |
public static class Utils | |
{ | |
public static int ParseIntInvariant(string s) => int.Parse(s, NumberFormatInfo.InvariantInfo); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment