using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using Cosmos.Build.Common; using Cosmos.Debug.Common; namespace Cosmos.Debug.Hosts { /// This class handles interactions with the Bochs emulation environment. public partial class Bochs : Host { /// The emulator process once started. private static Process _bochsProcess; /// The configuration file to be used when launching the Bochs virtual machine. private FileInfo _bochsConfigurationFile; /// Instanciation occurs when debugging engine is invoked to launch the process in suspended /// mode. Bochs process will eventually be launched later when debugging engine is instructed to /// Attach to the debugged process. public Bochs(Dictionary aParams, bool aUseGDB, FileInfo configurationFile, string harddisk = null) : base(aParams, aUseGDB) { if (null == configurationFile) { throw new ArgumentNullException("configurationFile"); } bool parseSucceeded = bool.TryParse(aParams[BuildPropertyNames.EnableBochsDebugString], out _useDebugVersion); parseSucceeded = bool.TryParse(aParams[BuildPropertyNames.StartBochsDebugGui], out startDebugGui); if (String.IsNullOrWhiteSpace(harddisk)) { mHarddiskFile = Path.Combine(CosmosPaths.Build, @"VMWare\Workstation\Filesystem.vmdk"); } else { mHarddiskFile = harddisk; } InitializeKeyValues(); GenerateConfiguration(configurationFile.FullName); _bochsConfigurationFile = configurationFile; } private bool _useDebugVersion; private bool startDebugGui; /// Fix the content of the configuration file, replacing each of the symbolic variable occurence /// with its associated value. /// A set of key/value pairs where the key is the name of a variable. The value is /// used for variable replacement. Variables are case sensistive. internal void FixBochsConfiguration(KeyValuePair[] symbols) { if ((null == symbols) || (0 == symbols.Length)) { return; } string content; using (StreamReader reader = new StreamReader(File.Open(_bochsConfigurationFile.FullName, FileMode.Open, FileAccess.Read))) { content = reader.ReadToEnd(); } foreach (KeyValuePair pair in symbols) { string variableName = string.Format("$({0})", pair.Key); content.Replace(variableName, pair.Value); } using (StreamWriter writer = new StreamWriter(File.Open(_bochsConfigurationFile.FullName, FileMode.Create, FileAccess.Write))) { writer.Write(content); } } public bool RedirectOutput = false; public Action LogOutput; public Action LogError; /// Initialize and start the Bochs process. public override void Start() { BochsSupport.ExtractBochsDebugSymbols(Path.ChangeExtension(mParams["ISOFile"], "map"), Path.ChangeExtension(mParams["ISOFile"], "sym")); _bochsProcess = new Process(); ProcessStartInfo _bochsStartInfo = _bochsProcess.StartInfo; _bochsStartInfo.FileName = (_useDebugVersion && BochsSupport.BochsDebugExe.Exists) ? BochsSupport.BochsDebugExe.FullName : BochsSupport.BochsExe.FullName; // Start Bochs without displaying the configuration interface (-q) and using the specified // configuration file (-f). The user is intended to edit the configuration file coming with // the Cosmos project whenever she wants to modify the environment. var xExtraLog = ""; if (_useDebugVersion) { //xExtraLog = "-dbglog \"bochsdbg.log\""; } _bochsStartInfo.Arguments = string.Format("-q {1} -f \"{0}\"", _bochsConfigurationFile.FullName, xExtraLog); _bochsStartInfo.WorkingDirectory = _bochsConfigurationFile.Directory.FullName; _bochsStartInfo.CreateNoWindow = true; // when ProcessStartInfo.UseShellExecute is supported in .net core, maybe this line isn't needed //_bochsStartInfo.UseShellExecute = true; if (RedirectOutput) { if (LogOutput == null) { throw new Exception("No LogOutput handler specified!"); } if (LogError == null) { throw new Exception("No LogError handler specified!"); } //_bochsStartInfo.RedirectStandardOutput = true; //_bochsStartInfo.RedirectStandardError = true; // _bochsProcess.OutputDataReceived += (sender, args) => LogOutput(args.Data); // _bochsProcess.ErrorDataReceived += (sender, args) => LogError(args.Data); } // Register for process completion event so that we can funnel it to any code that // subscribed to this event in our base class. _bochsProcess.EnableRaisingEvents = true; _bochsProcess.Exited += ExitCallback; _bochsProcess.Start(); if (RedirectOutput) { _bochsProcess.BeginErrorReadLine(); _bochsProcess.BeginOutputReadLine(); } return; } private void ExitCallback(object sender, EventArgs e) { if (null != OnShutDown) { try { OnShutDown(sender, e); } catch { } } } public override void Stop() { if (null != _bochsProcess) { try { _bochsProcess.Kill(); } catch { } } CleanUp(); } private void CleanUp() { OnShutDown(this, null); _bochsProcess.Exited -= ExitCallback; // TODO BlueSkeye : What kind of garbage may Bochs have left for us to clean ? } } }