Visual Studio Debugger
デバッグツール
Visual Studio Debugger
概要
Visual Studio Debuggerは、Microsoft Visual Studioの統合デバッガーです。.NET、C++、JavaScript等に対応し、ブレークポイント、ウォッチ、コールスタック分析などの強力な機能を提供します。
詳細
Visual Studio Debuggerは、1997年のVisual Studio 97から継続的に発展してきたMicrosoftの統合開発環境(IDE)に組み込まれたデバッグシステムです。.NET Framework、.NET Core、.NET 5+、C++、JavaScript、TypeScript、Python、Node.jsなど、幅広い言語とプラットフォームをサポートし、Windows開発環境における標準的なデバッガーとしての地位を確立しています。
最大の特徴は、Visual Studioエディターとの完璧な統合による直感的なデバッグ体験です。ソースコードエディター内で直接ブレークポイントを設定し、変数の値をホバーで確認、インラインでの値表示、コードレンズによる参照情報表示など、コーディングとデバッグをシームレスに行えます。IntelliSenseとの統合により、デバッグ中でも強力なオートコンプリートとコード分析機能を利用できます。
Windows開発において特に強力なのは、Win32 API、COM、WinRT、UWPなどのWindows固有技術のデバッグサポートです。マネージドコードとネイティブコードの混在(Mixed Mode)デバッグ、SQL Server統合によるストアドプロシージャデバッグ、Azure統合によるクラウドアプリケーションデバッグなど、Microsoftエコシステム全体をカバーします。
.NET Coreのクロスプラットフォーム対応により、macOSやLinuxでも利用可能になり、Visual Studio for Mac、Visual Studio Codeでも同等の機能を提供しています。Docker環境、Kubernetes、Azure Container Instancesでのデバッグサポートも充実し、現代的なクラウドネイティブ開発にも対応しています。
2024年現在では、.NET 8、C# 12、Visual Studio 2022の最新機能として、Hot Reload、Edit and Continue、AI支援による例外分析、パフォーマンス プロファイラーとの統合などが追加され、開発者生産性の向上に大きく貢献しています。
メリット・デメリット
メリット
- IDE完全統合: エディターとの完璧な統合による直感的操作
- 幅広い言語サポート: .NET、C++、JavaScript、Python等の多言語対応
- 強力なVisual機能: データ視覚化、グラフィカルなコールスタック表示
- Microsoftエコシステム: Azure、SQL Server、Office等との統合
- 混在モードデバッグ: マネージドとネイティブコードの同時デバッグ
- Hot Reload: コード変更の即座反映
- 豊富なウィンドウ: Watch、Locals、Call Stack等の専用ウィンドウ
- エンタープライズ機能: Team Foundation Server、DevOps統合
デメリット
- Windowsメイン: 主にWindows環境に特化
- 重いリソース: 高いメモリとCPU使用量
- ライセンス費用: Professional/Enterprise版は有料
- 学習コスト: 豊富な機能による初心者の習得困難
- 起動時間: 大規模IDEのため起動が遅い
- プラットフォーム制限: 一部機能はWindows限定
- バージョン依存: .NETバージョンとの互換性要件
主要リンク
- Visual Studio公式サイト
- Visual Studio Debugger公式ドキュメント
- Visual Studio デバッグ入門
- .NET デバッグ ガイド
- Visual Studio 2022 新機能
- Azure デバッグ
書き方の例
基本的なデバッグ設定と開始
// Program.cs - デバッグ対象のC#アプリケーション
using System;
using System.Collections.Generic;
using System.Linq;
namespace DebuggingExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Visual Studio Debuggerサンプル");
// ここにブレークポイント設定(F9 または 行番号クリック)
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var result = ProcessNumbers(numbers);
Console.WriteLine($"処理結果: {result}");
// 例外発生コード(デバッグ対象)
try
{
var divisionResult = DivideNumbers(10, 0);
Console.WriteLine($"除算結果: {divisionResult}");
}
catch (Exception ex)
{
// 例外発生時のデバッグ
Console.WriteLine($"エラー: {ex.Message}");
}
Console.ReadLine();
}
static int ProcessNumbers(List<int> numbers)
{
int sum = 0;
// Watch ウィンドウで監視対象変数
foreach (var number in numbers)
{
sum += number * 2; // ステップイン対象
}
return sum;
}
static double DivideNumbers(double a, double b)
{
// 条件付きブレークポイント設定可能
// 条件: b == 0
if (b == 0)
throw new DivideByZeroException("0で除算はできません");
return a / b;
}
}
}
/* デバッグ実行方法:
* 1. F5 (デバッグ開始)
* 2. Ctrl+F5 (デバッグなしで実行)
* 3. F9 (ブレークポイント設定/解除)
* 4. F10 (ステップオーバー)
* 5. F11 (ステップイン)
* 6. Shift+F11 (ステップアウト)
*/
高度なブレークポイント機能
// AdvancedBreakpoints.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
public class AdvancedBreakpoints
{
public void DemonstrateBreakpoints()
{
var data = GenerateTestData();
for (int i = 0; i < data.Count; i++)
{
// 条件付きブレークポイント例
// 右クリック → 条件... → i > 5
ProcessItem(data[i], i);
}
}
private List<string> GenerateTestData()
{
return new List<string>
{
"Apple", "Banana", "Cherry", "Date", "Elderberry",
"Fig", "Grape", "Honeydew", "Kiwi", "Lemon"
};
}
private void ProcessItem(string item, int index)
{
// ヒットカウント ブレークポイント
// 右クリック → 条件... → ヒットカウント → 3回目
Debug.WriteLine($"Processing item {index}: {item}");
// トレースポイント(ログ出力のみ、停止しない)
// 右クリック → アクション... → メッセージをログに出力
// メッセージ: "Item: {item}, Index: {index}"
if (item.Length > 5)
{
// フィルター付きブレークポイント
// 右クリック → 条件... → フィルター → ThreadId == 1
ProcessLongName(item);
}
}
private void ProcessLongName(string name)
{
// 関数ブレークポイント
// デバッグ → 新しいブレークポイント → 関数ブレークポイント
// 関数名: AdvancedBreakpoints.ProcessLongName
var reversedName = new string(name.Reverse().ToArray());
Console.WriteLine($"Reversed: {reversedName}");
}
}
// デバッグ時の便利なキーボードショートカット:
// F9: ブレークポイント設定/解除
// Ctrl+Shift+F9: 全ブレークポイント削除
// Ctrl+B: ブレークポイント ウィンドウ表示
// F5: 続行
// Shift+F5: デバッグ停止
// Ctrl+Shift+F5: 再開(リスタート)
ウォッチとローカル変数の監視
// WatchExample.cs
using System;
using System.Collections.Generic;
using System.Linq;
public class WatchExample
{
private List<Customer> customers;
private decimal totalRevenue;
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Balance { get; set; }
public DateTime LastLogin { get; set; }
public bool IsActive { get; set; }
}
public void AnalyzeCustomers()
{
InitializeCustomers();
// ウォッチ ウィンドウで監視する式の例:
// customers.Count
// customers.Where(c => c.IsActive).Count()
// totalRevenue
// customers.Average(c => c.Balance)
foreach (var customer in customers)
{
// ローカル ウィンドウで 'customer' の詳細確認
if (customer.IsActive)
{
totalRevenue += customer.Balance;
// クイック ウォッチ (Shift+F9) で式を評価
var daysSinceLogin = (DateTime.Now - customer.LastLogin).Days;
if (daysSinceLogin > 30)
{
// 自動変数ウィンドウで現在のスコープの変数確認
SendReactivationEmail(customer);
}
}
}
// イミディエイト ウィンドウでの対話的デバッグ
// Ctrl+Alt+I でイミディエイト ウィンドウ表示
// 例: ? customers.Count()
// 例: ? totalRevenue.ToString("C")
GenerateReport();
}
private void InitializeCustomers()
{
customers = new List<Customer>
{
new Customer { Id = 1, Name = "Alice", Balance = 1000.00m,
LastLogin = DateTime.Now.AddDays(-10), IsActive = true },
new Customer { Id = 2, Name = "Bob", Balance = 2500.50m,
LastLogin = DateTime.Now.AddDays(-45), IsActive = true },
new Customer { Id = 3, Name = "Charlie", Balance = 500.75m,
LastLogin = DateTime.Now.AddDays(-60), IsActive = false }
};
}
private void SendReactivationEmail(Customer customer)
{
// デバッガー表示属性の使用例
[DebuggerDisplay("Email sent to {customer.Name} ({customer.Id})")]
void LogEmailSent()
{
Console.WriteLine($"Reactivation email sent to {customer.Name}");
}
LogEmailSent();
}
private void GenerateReport()
{
var activeCustomers = customers.Count(c => c.IsActive);
var averageBalance = customers.Where(c => c.IsActive).Average(c => c.Balance);
// デバッガー変数ウィンドウのカスタム表示
[DebuggerDisplay("Active: {activeCustomers}, Avg Balance: {averageBalance:C}")]
var report = new
{
ActiveCustomers = activeCustomers,
TotalRevenue = totalRevenue,
AverageBalance = averageBalance
};
Console.WriteLine($"Report: {report}");
}
}
例外処理とデバッグ設定
// ExceptionHandling.cs
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
public class ExceptionHandling
{
public async Task DemonstrateExceptionDebugging()
{
// 例外設定: デバッグ → ウィンドウ → 例外設定
// Common Language Runtime Exceptions →
// - System.DivideByZeroException (スローされたときに中断)
// - System.FileNotFoundException (ユーザーが処理しない場合に中断)
try
{
await ProcessFileOperations();
}
catch (FileNotFoundException ex)
{
// 例外発生時の詳細情報確認
// - Inner Exception
// - Stack Trace
// - Data Collection
Console.WriteLine($"ファイルエラー: {ex.Message}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"ネットワークエラー: {ex.Message}");
}
catch (Exception ex)
{
// 例外ヘルパーで詳細分析
Console.WriteLine($"予期しないエラー: {ex.Message}");
}
}
private async Task ProcessFileOperations()
{
// ファイル操作での例外デバッグ
var filePath = @"C:\NonExistentFile.txt";
// 第一回例外でブレーク設定時にここで停止
var content = await File.ReadAllTextAsync(filePath);
// HTTP 操作での例外デバッグ
using var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(1); // 意図的にタイムアウト設定
var response = await client.GetAsync("https://httpstat.us/500");
var responseContent = await response.Content.ReadAsStringAsync();
}
// カスタム例外の作成とデバッグ
public class BusinessLogicException : Exception
{
public string ErrorCode { get; }
public object Context { get; }
public BusinessLogicException(string message, string errorCode, object context = null)
: base(message)
{
ErrorCode = errorCode;
Context = context;
}
}
public void BusinessProcess(int value)
{
if (value < 0)
{
// カスタム例外のデバッグ情報
throw new BusinessLogicException(
"負の値は許可されていません",
"NEGATIVE_VALUE",
new { InputValue = value, Timestamp = DateTime.Now }
);
}
// ビジネスロジックの処理...
}
}
非同期・並行処理のデバッグ
// AsyncDebugging.cs
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class AsyncDebugging
{
public async Task DemonstrateAsyncDebugging()
{
// 並列タスクウィンドウ: デバッグ → ウィンドウ → 並列タスク
// 並列スタックウィンドウ: デバッグ → ウィンドウ → 並列スタック
var tasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
int taskId = i; // クロージャキャプチャ
tasks.Add(ProcessDataAsync(taskId));
}
// 全タスクの完了待機
await Task.WhenAll(tasks);
// デッドロックの例(デバッグで確認)
try
{
await DemonstrateDeadlock();
}
catch (Exception ex)
{
Console.WriteLine($"デッドロック例外: {ex.Message}");
}
}
private async Task ProcessDataAsync(int taskId)
{
// 並列タスクウィンドウでタスク状態確認
Console.WriteLine($"Task {taskId} started on thread {Thread.CurrentThread.ManagedThreadId}");
// 非同期操作のシミュレーション
await Task.Delay(1000);
// CPU集約的処理
var result = await Task.Run(() => CalculateHeavyOperation(taskId));
Console.WriteLine($"Task {taskId} completed with result: {result}");
}
private int CalculateHeavyOperation(int input)
{
// 呼び出し履歴ウィンドウで非同期コールスタック確認
int result = 0;
for (int i = 0; i < 1000000; i++)
{
result += (input * i) % 1000;
}
return result;
}
// デッドロック検出の例
private readonly object lockA = new object();
private readonly object lockB = new object();
private async Task DemonstrateDeadlock()
{
var task1 = Task.Run(() =>
{
lock (lockA)
{
Thread.Sleep(100);
lock (lockB)
{
Console.WriteLine("Task 1 completed");
}
}
});
var task2 = Task.Run(() =>
{
lock (lockB)
{
Thread.Sleep(100);
lock (lockA)
{
Console.WriteLine("Task 2 completed");
}
}
});
// デッドロック発生時は並列スタックウィンドウで確認
await Task.WhenAll(task1, task2);
}
// async/await での例外伝播デバッグ
public async Task ChainedAsyncCalls()
{
try
{
await Level1Async();
}
catch (Exception ex)
{
// 非同期コールスタックの確認
// - AggregateException の内部例外
// - 元の例外発生箇所の特定
Console.WriteLine($"Caught exception: {ex.Message}");
}
}
private async Task Level1Async()
{
await Level2Async();
}
private async Task Level2Async()
{
await Level3Async();
}
private async Task Level3Async()
{
// ここで例外発生
await Task.Delay(100);
throw new InvalidOperationException("Level 3 で例外が発生しました");
}
}
パフォーマンス プロファイラーとの統合
// PerformanceProfiling.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
public class PerformanceProfiling
{
// 診断ツール: デバッグ → ウィンドウ → 診断ツール表示
// CPU使用率、メモリ使用量をリアルタイム監視
public void DemonstratePerformanceProfiling()
{
// メモリ使用量スナップショット取得ポイント
var stopwatch = Stopwatch.StartNew();
// CPU集約的処理
PerformCpuIntensiveTask();
stopwatch.Stop();
Console.WriteLine($"CPU集約的処理: {stopwatch.ElapsedMilliseconds}ms");
// メモリ集約的処理
stopwatch.Restart();
PerformMemoryIntensiveTask();
stopwatch.Stop();
Console.WriteLine($"メモリ集約的処理: {stopwatch.ElapsedMilliseconds}ms");
// ガベージコレクション強制実行(メモリプロファイリング用)
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
private void PerformCpuIntensiveTask()
{
// CPU使用率監視対象
var result = 0.0;
for (int i = 0; i < 10000000; i++)
{
result += Math.Sqrt(i) * Math.Sin(i);
}
Console.WriteLine($"CPU計算結果: {result:F2}");
}
private void PerformMemoryIntensiveTask()
{
// ヒープメモリ使用量監視対象
var data = new List<byte[]>();
for (int i = 0; i < 1000; i++)
{
// 大きなバイト配列を作成(メモリプレッシャー)
var largeArray = new byte[1024 * 1024]; // 1MB
// パターンで埋める
for (int j = 0; j < largeArray.Length; j += 1024)
{
largeArray[j] = (byte)(i % 256);
}
data.Add(largeArray);
}
Console.WriteLine($"作成した配列数: {data.Count}");
// メモリ使用量をプロファイラーで確認
var totalMemory = GC.GetTotalMemory(false);
Console.WriteLine($"現在のメモリ使用量: {totalMemory / 1024 / 1024} MB");
}
// ETW (Event Tracing for Windows) イベント
[Conditional("DEBUG")]
public void LogPerformanceEvent(string eventName, object data)
{
// パフォーマンス カウンターでの計測
Debug.WriteLine($"[PERF] {eventName}: {data}");
}
}
Visual Studio 設定とカスタマイズ
// DebuggerCustomization.cs
using System;
using System.Diagnostics;
// デバッガー表示属性の活用
[DebuggerDisplay("User: {Name} (ID: {Id}, Active: {IsActive})")]
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public string InternalSecret { get; set; } // デバッガーで非表示
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public Dictionary<string, object> Properties { get; set; } // 折りたたみ表示
}
// デバッガー型プロキシ
[DebuggerTypeProxy(typeof(UserCollectionDebugView))]
public class UserCollection : List<User>
{
public int ActiveUserCount => this.Count(u => u.IsActive);
}
// カスタムデバッガー表示
internal class UserCollectionDebugView
{
private readonly UserCollection collection;
public UserCollectionDebugView(UserCollection collection)
{
this.collection = collection;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public User[] Users => collection.ToArray();
public int TotalUsers => collection.Count;
public int ActiveUsers => collection.ActiveUserCount;
}
/*
Visual Studio デバッガー設定のカスタマイズ:
1. ツール → オプション → デバッグ → 全般
- "すべてのモジュールに対してJust My Codeを有効にする"
- "ソース サーバー サポートを有効にする"
- "Edit and Continue を有効にする"
2. ツール → オプション → デバッグ → 出力ウィンドウ
- プログラム出力、例外メッセージの表示設定
3. ツール → オプション → デバッグ → シンボル
- シンボルファイルの場所とキャッシュ設定
4. ブレークポイントのエクスポート/インポート
- デバッグ → ウィンドウ → ブレークポイント → エクスポート
5. カスタム視覚化ツール(.natvis ファイル)
- 複雑なデータ構造の表示カスタマイズ
*/