Commit 175a5410 authored by Hirsch Singhal's avatar Hirsch Singhal Committed by GitHub
Browse files

App, process, and crash dump collection APIs (#193)

* Add usermode crash dump APIs

* Additional fixes to DumpCollection

* Add kernel, live, and app dump APIs.

* StyleCop pass
parent d1941d98
Loading
Loading
Loading
Loading
+223 −0
Original line number Diff line number Diff line
//----------------------------------------------------------------------------------------------
// <copyright file="AppCrashDumpCollection.cs" company="Microsoft Corporation">
//     Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;

namespace Microsoft.Tools.WindowsDevicePortal
{
    /// <content>
    /// Wrappers for app crash dump collection methods.
    /// </content>
    public partial class DevicePortal
    {
        /// <summary>
        /// API to retrieve list of the available crash dumps (for sideloaded applications).
        /// </summary>
        public static readonly string AvailableCrashDumpsApi = "api/debug/dump/usermode/dumps";

        /// <summary>
        /// API to download or delete a crash dump file (for a sideloaded application).
        /// </summary>
        public static readonly string CrashDumpFileApi = "api/debug/dump/usermode/crashdump";

        /// <summary>
        /// API to control the crash dump settings for a sideloaded application.
        /// </summary>
        public static readonly string CrashDumpSettingsApi = "api/debug/dump/usermode/crashcontrol";

        /// <summary>
        /// Get a list of app crash dumps on the device. 
        /// </summary>
        /// <returns>List of AppCrashDump objects, which represent crashdumps on the device. </returns>
        public async Task<List<AppCrashDump>> GetAppCrashDumpListAsync()
        {
            AppCrashDumpList cdl = await this.GetAsync<AppCrashDumpList>(AvailableCrashDumpsApi);
            return cdl.CrashDumps;
        }

        /// <summary>
        /// Download a sideloaded app's crash dump.  
        /// </summary>
        /// <param name="crashdump"> The AppCrashDump to download</param>
        /// <returns>Stream of the crash dump</returns>
        public async Task<Stream> GetAppCrashDumpAsync(AppCrashDump crashdump)
        {
            string queryString = CrashDumpFileApi + string.Format("?packageFullName={0}&fileName={1}", crashdump.PackageFullName, crashdump.Filename);
            Uri uri = Utilities.BuildEndpoint(
                this.deviceConnection.Connection,
                queryString);

            return await this.GetAsync(uri);
        }

        /// <summary>
        /// Delete an app crash dump stored on the device. 
        /// </summary>
        /// <param name="crashdump">The crashdump to be deleted</param>
        /// <returns>Task tracking completion of the request.</returns>
        public async Task DeleteAppCrashDumpAsync(AppCrashDump crashdump)
        {
            await this.DeleteAsync(CrashDumpFileApi,
                string.Format("packageFullName={0}&fileName={1}", crashdump.PackageFullName, crashdump.Filename));
        }

        /// <summary>
        /// Get the crash settings for a sideloaded app. 
        /// </summary>
        /// <param name="app">The app to get settings for</param>
        /// <returns>The crash settings for the app</returns>
        public async Task<AppCrashDumpSettings> GetAppCrashDumpSettingsAsync(AppPackage app)
        {
            return await this.GetAppCrashDumpSettingsAsync(app.PackageFullName);
        }

        /// <summary>
        /// Get the crash settings for a sideloaded app. 
        /// </summary>
        /// <param name="packageFullname">The app to get settings for</param>
        /// <returns>The crash settings for the app</returns>
        public async Task<AppCrashDumpSettings> GetAppCrashDumpSettingsAsync(string packageFullname)
        {
            return await this.GetAsync<AppCrashDumpSettings>(
                CrashDumpSettingsApi,
                string.Format("packageFullname={0}", packageFullname));
        }

        /// <summary>
        /// Set the crash settings for a sideloaded app. 
        /// </summary>
        /// <param name="app">The app to set crash settings for.</param>
        /// <param name="enable">Whether to enable or disable crash collection for the app. </param>
        /// <returns>Task tracking completion of the request.</returns>
        public async Task SetAppCrashDumpSettingsAsync(AppPackage app, bool enable = true)
        {
            string pfn = app.PackageFullName;
            await SetAppCrashDumpSettingsAsync(pfn, enable);
        }

        /// <summary>
        /// Set the crash settings for a sideloaded app. 
        /// </summary>
        /// <param name="packageFullname">The app to set crash settings for.</param>
        /// <param name="enable">Whether to enable or disable crash collection for the app. </param>
        /// <returns>Task tracking completion of the request.</returns>
        public async Task SetAppCrashDumpSettingsAsync(string packageFullName, bool enable = true)
        {
            if (enable)
            {
                await this.PostAsync(
                    CrashDumpSettingsApi,
                string.Format("packageFullName={0}", packageFullName));
            }
            else
            {
                await this.DeleteAsync(
                    CrashDumpSettingsApi,
                string.Format("packageFullName={0}", packageFullName));
            }
        }

        #region Data contract

        /// <summary>
        /// Per-app crash dump settings.
        /// </summary>
        [DataContract]
        public class AppCrashDumpSettings
        {
            /// <summary>
            /// Gets whether crash dumps are enabled for the app
            /// </summary>
            [DataMember(Name = "CrashDumpEnabled")]
            public bool CrashDumpEnabled
            {
                get;
                private set;
            }
        }

        /// <summary>
        /// Represents a crash dump collected from a sideloaded app. 
        /// </summary>
        [DataContract]
        public class AppCrashDump 
        {

            /// <summary>
            /// Gets the timestamp of the crash as a string.
            /// </summary>
            [DataMember(Name = "FileDate")]
            public string FileDateAsString
            {
                get;
                private set;
            }

            /// <summary>
            /// Gets the timestamp of the crash.
            /// </summary>
            public DateTime FileDate
            {
                get
                {
                    return DateTime.Parse(this.FileDateAsString);
                }
            }

            /// <summary>
            /// Gets the filename of the crash file. 
            /// </summary>
            [DataMember(Name = "FileName")]
            public string Filename
            {
                get;
                private set;
            }

            /// <summary>
            /// Gets the size of the crash dump, in bytes
            /// </summary>
            [DataMember(Name = "FileSize")]
            public uint FileSizeInBytes
            {
                get;
                private set;
            }

            /// <summary>
            /// Gets the package full name of the app that crashed. 
            /// </summary>
            [DataMember(Name = "PackageFullName")]
            public string PackageFullName
            {
                get;
                private set;
            }
        }

        /// <summary>
        /// A list of crash dumps.  Internal usage only. 
        /// </summary>
        [DataContract]
        private class AppCrashDumpList
        {
            /// <summary>
            /// Gets a list of crash dumps on the device. 
            /// </summary>
            [DataMember(Name = "CrashDumps")]
            public List<AppCrashDump> CrashDumps
            {
                get;
                private set;
            }
        }
        #endregion Data contract
    }
}
+199 −14
Original line number Diff line number Diff line
@@ -4,6 +4,12 @@
// </copyright>
//----------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Threading.Tasks;

namespace Microsoft.Tools.WindowsDevicePortal
{
    /// <content>
@@ -14,12 +20,7 @@ namespace Microsoft.Tools.WindowsDevicePortal
        /// <summary>
        /// API to retrieve list of the available bugcheck minidumps.
        /// </summary>
        public static readonly string AvailableBugChecksApi = "api/debug/dump/kernel/dumplish";

        /// <summary>
        /// API to retrieve list of the available crash dumps (for sideloaded applications).
        /// </summary>
        public static readonly string AvailableCrashDumpsApi = "api/debug/dump/usermode/dumps";
        public static readonly string AvailableBugChecksApi = "api/debug/dump/kernel/dumplist";

        /// <summary>
        /// API to download a bugcheck minidump file.
@@ -32,23 +33,207 @@ namespace Microsoft.Tools.WindowsDevicePortal
        public static readonly string BugcheckSettingsApi = "api/debug/dump/kernel/crashcontrol";

        /// <summary>
        /// API to download or delete a crash dump file (for a sideloaded application).
        /// API to retrieve a live kernel dump.
        /// </summary>
        public static readonly string CrashDumpFileApi = "api/debug/dump/usermode/crashdump";
        public static readonly string LiveKernelDumpApi = "api/debug/dump/livekernel";

        /// <summary>
        /// API to control the crash dump settings for a sideloaded application.
        /// API to retrieve a live dump from a running user mode process.
        /// </summary>
        public static readonly string CrashDumpSettingsApi = "api/debug/dump/usermode/crashcontrol";
        public static readonly string LiveProcessDumpApi = "api/debug/dump/usermode/live";

        /// <summary>
        /// API to retrieve a live kernel dump.
        /// Get a list of dumpfiles on the system. 
        /// </summary>
        public static readonly string LiveKernelDumpApi = "api/debug/dump/livekernel";
        /// <returns>List of Dumpfiles.  Use GetDumpFile to download the file. </returns>
        public async Task<List<Dumpfile>> GetDumpfileListAsync()
        {
            DumpFileList dfl = await this.GetAsync<DumpFileList>(AvailableBugChecksApi);
            return dfl.DumpFiles;
        }

        /// <summary>
        /// API to retrieve a live dump from a running user mode process.
        /// Download a dumpfile from the system. 
        /// </summary>
        public static readonly string LiveProcessDumpApi = "api/debug/dump/usermode/live";
        /// <param name="crashdump"> Dumpfile object to be downloaded</param>
        /// <returns>Stream of the dump file </returns>
        public async Task<Stream> GetDumpFileAsync(Dumpfile crashdump)
        {
            string queryString = BugcheckFileApi + string.Format("?filename={0}", crashdump.Filename);
            Uri uri = Utilities.BuildEndpoint(
                this.deviceConnection.Connection,
                queryString);

            return await this.GetAsync(uri);
        }

        /// <summary>
        /// Takes a live kernel dump of the device.  This does not reboot the device. 
        /// </summary>
        /// <returns>Stream of the kernel dump</returns>
        public async Task<Stream> GetLiveKernelDumpAsync()
        {
            Uri uri = Utilities.BuildEndpoint(
                this.deviceConnection.Connection,
                LiveKernelDumpApi);

            return await this.GetAsync(uri);
        }

        /// <summary>
        /// Take a live dump of a process on the system. 
        /// </summary>
        /// <param name="pid"> PID of the process to get a live dump of.</param>
        /// <returns>Stream of the process live dump</returns>
        public async Task<Stream> GetLiveProcessDumpAsync(int pid)
        {
            string queryString = LiveProcessDumpApi + string.Format("?pid={0}", pid);
            Uri uri = Utilities.BuildEndpoint(
                this.deviceConnection.Connection,
                queryString);

            return await this.GetAsync(uri);
        }

        /// <summary>
        /// Get information on how and when dump files are saved on the device. 
        /// </summary>
        /// <returns>Dumpfile settings object.  This can be edited and returned to the device to alter the settings.</returns>
        public async Task<DumpFileSettings> GetDumpFileSettingsAsync()
        {
            return await this.GetAsync<DumpFileSettings>(BugcheckSettingsApi);
        }

        /// <summary>
        /// Set how and when dump files are saved on the device. 
        /// </summary>
        /// <param name="dfs">Altered DumpFileSettings object to set on the device</param>
        /// <returns>Task tracking completion of the request</returns>
        public async Task SetDumpFileSettingsAsync(DumpFileSettings dfs)
        {
            await this.PostAsync(
                BugcheckSettingsApi,
                string.Format(
                    "autoreboot={0}&overwrite={1}&dumptype={2}&maxdumpcount={3}",
                    dfs.AutoReboot ? "1" : "0", dfs.Overwrite ? "1" : "0", (int)dfs.DumpType, dfs.MaxDumpCount));
        }
    }

    #region Data Contract

    /// <summary>
    /// DumpFileSettings object.  Used to get and set how and when a dump is saved on the device. 
    /// </summary>
    [DataContract]
    public class DumpFileSettings
    {
        /// <summary>
        /// Gets or sets whether the device should restart after a crash dump is taken.
        /// </summary>
        [DataMember(Name = "autoreboot")]
        public bool AutoReboot { get; set; }

        /// <summary>
        /// Gets or sets the type of dump to be saved when a bugcheck occurs.  
        /// </summary>
        [DataMember(Name = "dumptype")]
        public DumpTypes DumpType { get; set; }

        /// <summary>
        /// Gets or sets the max number of dumps to be saved on the device. 
        /// </summary>
        [DataMember(Name = "maxdumpcount")]
        public int MaxDumpCount { get; set; }

        /// <summary>
        /// Gets or sets whether new dumps should overwrite older dumps. 
        /// </summary>
        [DataMember(Name = "overwrite")]
        public bool Overwrite { get; set; }

        /// <summary>
        /// The 3 types of dumps  that can be saved on the device (or not saved at all). 
        /// </summary>
        public enum DumpTypes
        {
            /// <summary>
            /// Don't collect device crash dumps
            /// </summary>
            Disabled = 0,
            
            /// <summary>
            /// Collect all in use memory
            /// </summary>
            CompleteMemoryDump = 1,

            /// <summary>
            /// Don't include usermode memory in the dump
            /// </summary>
            KernelDump = 2,
            
            /// <summary>
            /// Limited kernel dump
            /// </summary>
            Minidump = 3
        }
    }

    /// <summary>
    /// Gets a list of kernel dumps on the device. 
    /// </summary>
    [DataContract]
    public class DumpFileList
    {
        /// <summary>
        /// Gets a list of kernel dumps on the device. 
        /// </summary>
        [DataMember(Name = "DumpFiles")]
        public List<Dumpfile> DumpFiles { get; private set; }
    }

    /// <summary>
    /// Represents a dumpfile stored on the device. 
    /// </summary>
    [DataContract]
    public class Dumpfile
    {
        /// <summary>
        /// Gets the timestamp of the crash as a string.
        /// </summary>
        [DataMember(Name = "FileDate")]
        public string FileDateAsString
        {
            get; private set;
        }

        /// <summary>
        /// Gets the timestamp of the crash.
        /// </summary>
        public DateTime FileDate
        {
            get
            {
                return DateTime.Parse(this.FileDateAsString);
            }
        }

        /// <summary>
        /// Gets the filename of the crash file. 
        /// </summary>
        [DataMember(Name = "FileName")]
        public string Filename
        {
            get; private set;
        }

        /// <summary>
        /// Gets the size of the crash dump, in bytes
        /// </summary>
        [DataMember(Name = "FileSize")]
        public uint FileSizeInBytes
        {
            get; private set;
        }
    }
    #endregion Data Contract
}
+1 −1
Original line number Diff line number Diff line
@@ -117,7 +117,7 @@ namespace Microsoft.Tools.WindowsDevicePortal
            public string DefaultApp { get; private set; }

            /// <summary>
            /// Gets or sets the application packages
            /// Gets the application packages
            /// </summary>
            [DataMember(Name = "AppPackages")]
            public List<AppPackage> AppPackages { get; private set; }
+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
    <Import_RootNamespace>Microsoft.Tools.WindowsDevicePortal</Import_RootNamespace>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="$(MSBuildThisFileDirectory)Core\AppCrashDumpCollection.cs" />
    <Compile Include="$(MSBuildThisFileDirectory)Core\AppDeployment.cs" />
    <Compile Include="$(MSBuildThisFileDirectory)Core\AppFileExplorer.cs" />
    <Compile Include="$(MSBuildThisFileDirectory)Core\DeviceManager.cs" />