diff --git a/BenchmarkDotNet.sln b/BenchmarkDotNet.sln
index eed29c80af..d51cb4e029 100644
--- a/BenchmarkDotNet.sln
+++ b/BenchmarkDotNet.sln
@@ -53,6 +53,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Integration
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.TestAdapter", "src\BenchmarkDotNet.TestAdapter\BenchmarkDotNet.TestAdapter.csproj", "{4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Diagnostics.dotMemory", "src\BenchmarkDotNet.Diagnostics.dotMemory\BenchmarkDotNet.Diagnostics.dotMemory.csproj", "{2E2283A3-6DA6-4482-8518-99D6D9F689AB}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -143,6 +145,10 @@ Global
 		{4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2E2283A3-6DA6-4482-8518-99D6D9F689AB}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -169,6 +175,7 @@ Global
 		{C5BDA61F-3A56-4B59-901D-0A17E78F4076} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
 		{AACA2C63-A85B-47AB-99FC-72C3FF408B14} = {14195214-591A-45B7-851A-19D3BA2413F9}
 		{4C9C89B8-7C4E-4ECF-B3C9-324C8772EDAC} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
+		{2E2283A3-6DA6-4482-8518-99D6D9F689AB} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F}
diff --git a/docs/articles/guides/nuget.md b/docs/articles/guides/nuget.md
index 0e2de62a79..d1359c03d9 100644
--- a/docs/articles/guides/nuget.md
+++ b/docs/articles/guides/nuget.md
@@ -13,6 +13,7 @@ We have the following set of NuGet packages (you can install it directly from `n
 * `BenchmarkDotNet.Annotations`: Basic BenchmarkDotNet annotations for your benchmarks.
 * `BenchmarkDotNet.Diagnostics.Windows`: an additional optional package that provides a set of Windows diagnosers.
 * `BenchmarkDotNet.Diagnostics.dotTrace`: an additional optional package that provides DotTraceDiagnoser.
+* `BenchmarkDotNet.Diagnostics.dotMemory`: an additional optional package that provides DotMemoryDiagnoser.
 * `BenchmarkDotNet.Templates`: Templates for BenchmarkDotNet.
 
 You might find other NuGet packages that start with `BenchmarkDotNet` name, but they are internal BDN packages that should not be installed manually. All that matters are the three packages mentioned above.
diff --git a/docs/articles/samples/IntroDotMemoryDiagnoser.md b/docs/articles/samples/IntroDotMemoryDiagnoser.md
new file mode 100644
index 0000000000..e16e96a83c
--- /dev/null
+++ b/docs/articles/samples/IntroDotMemoryDiagnoser.md
@@ -0,0 +1,22 @@
+---
+uid: BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser
+---
+
+## Sample: IntroDotMemoryDiagnoser
+
+If you want to get a memory allocation profile of your benchmarks, just add the `[DotMemoryDiagnoser]` attribute, as shown below.
+As a result, BenchmarkDotNet performs bonus benchmark runs using attached
+  [dotMemory Command-Line Profiler](https://www.jetbrains.com/help/dotmemory/Working_with_dotMemory_Command-Line_Profiler.html).
+The obtained dotMemory workspaces are saved to the `artifacts` folder.
+These dotMemory workspaces can be opened using the [standalone dotMemory](https://www.jetbrains.com/dotmemory/),
+  or [dotMemory in Rider](https://www.jetbrains.com/help/rider/Memory_profiling_of_.NET_code.html).
+
+### Source code
+
+[!code-csharp[IntroDotMemoryDiagnoser.cs](../../../samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs)]
+
+### Links
+
+* The permanent link to this sample: @BenchmarkDotNet.Samples.IntroDotMemoryDiagnoser
+
+---
\ No newline at end of file
diff --git a/docs/articles/samples/toc.yml b/docs/articles/samples/toc.yml
index 8098a637c3..7e4c7671e1 100644
--- a/docs/articles/samples/toc.yml
+++ b/docs/articles/samples/toc.yml
@@ -38,6 +38,8 @@
   href: IntroDisassemblyRyuJit.md
 - name: IntroDotTraceDiagnoser
   href: IntroDotTraceDiagnoser.md
+- name: IntroDotMemoryDiagnoser
+  href: IntroDotMemoryDiagnoser.md
 - name: IntroEnvVars
   href: IntroEnvVars.md
 - name: IntroEventPipeProfiler
diff --git a/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj b/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj
index 1af13f61c3..36c3f60b32 100644
--- a/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj
+++ b/samples/BenchmarkDotNet.Samples/BenchmarkDotNet.Samples.csproj
@@ -26,6 +26,7 @@
   
   
     
+    
     
     
     
diff --git a/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs b/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs
new file mode 100644
index 0000000000..846e2178f4
--- /dev/null
+++ b/samples/BenchmarkDotNet.Samples/IntroDotMemoryDiagnoser.cs
@@ -0,0 +1,52 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Diagnostics.dotMemory;
+using System.Collections.Generic;
+
+namespace BenchmarkDotNet.Samples
+{
+    // Enables dotMemory profiling for all jobs
+    [DotMemoryDiagnoser]
+    // Adds the default "external-process" job
+    // Profiling is performed using dotMemory Command-Line Profiler
+    // See: https://www.jetbrains.com/help/dotmemory/Working_with_dotMemory_Command-Line_Profiler.html
+    [SimpleJob]
+    // Adds an "in-process" job
+    // Profiling is performed using dotMemory SelfApi
+    // NuGet reference: https://www.nuget.org/packages/JetBrains.Profiler.SelfApi
+    [InProcess]
+    public class IntroDotMemoryDiagnoser
+    {
+        [Params(1024)]
+        public int Size;
+
+        private byte[] dataArray;
+        private IEnumerable dataEnumerable;
+
+        [GlobalSetup]
+        public void Setup()
+        {
+            dataArray = new byte[Size];
+            dataEnumerable = dataArray;
+        }
+
+        [Benchmark]
+        public int IterateArray()
+        {
+            var count = 0;
+            foreach (var _ in dataArray)
+                count++;
+
+            return count;
+        }
+
+        [Benchmark]
+        public int IterateEnumerable()
+        {
+            var count = 0;
+            foreach (var _ in dataEnumerable)
+                count++;
+
+            return count;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj
new file mode 100644
index 0000000000..9ee6b4be18
--- /dev/null
+++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/BenchmarkDotNet.Diagnostics.dotMemory.csproj
@@ -0,0 +1,20 @@
+
+    
+    
+        net6.0;net462;netcoreapp3.1
+        $(NoWarn);1591
+        BenchmarkDotNet.Diagnostics.dotMemory
+        BenchmarkDotNet.Diagnostics.dotMemory
+        BenchmarkDotNet.Diagnostics.dotMemory
+        enable
+    
+
+    
+        
+    
+    
+    
+        
+    
+
+
diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs
new file mode 100644
index 0000000000..cbba52fb56
--- /dev/null
+++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using BenchmarkDotNet.Analysers;
+using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.Engines;
+using BenchmarkDotNet.Exporters;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Loggers;
+using BenchmarkDotNet.Portability;
+using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Validators;
+using RunMode = BenchmarkDotNet.Diagnosers.RunMode;
+
+namespace BenchmarkDotNet.Diagnostics.dotMemory
+{
+    public class DotMemoryDiagnoser : IProfiler
+    {
+        private readonly Uri? nugetUrl;
+        private readonly string? toolsDownloadFolder;
+
+        private DotMemoryTool? tool;
+
+        public DotMemoryDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null)
+        {
+            this.nugetUrl = nugetUrl;
+            this.toolsDownloadFolder = toolsDownloadFolder;
+        }
+
+        public IEnumerable Ids => new[] { "DotMemory" };
+        public string ShortName => "dotMemory";
+
+        public RunMode GetRunMode(BenchmarkCase benchmarkCase)
+        {
+            return IsSupported(benchmarkCase.Job.Environment.GetRuntime().RuntimeMoniker) ? RunMode.ExtraRun : RunMode.None;
+        }
+
+        private readonly List snapshotFilePaths = new ();
+
+        public void Handle(HostSignal signal, DiagnoserActionParameters parameters)
+        {
+            var logger = parameters.Config.GetCompositeLogger();
+            var job = parameters.BenchmarkCase.Job;
+
+            var runtimeMoniker = job.Environment.GetRuntime().RuntimeMoniker;
+            if (!IsSupported(runtimeMoniker))
+            {
+                logger.WriteLineError($"Runtime '{runtimeMoniker}' is not supported by dotMemory");
+                return;
+            }
+
+            switch (signal)
+            {
+                case HostSignal.BeforeAnythingElse:
+                    if (tool is not null)
+                        throw new InvalidOperationException("DotMemory tool is already initialized");
+                    tool = new DotMemoryTool(logger, nugetUrl, downloadTo: toolsDownloadFolder);
+                    tool.Init();
+                    break;
+                case HostSignal.BeforeActualRun:
+                    if (tool is null)
+                        throw new InvalidOperationException("DotMemory tool is not initialized");
+                    snapshotFilePaths.Add(tool.Start(parameters));
+                    break;
+                case HostSignal.AfterActualRun:
+                    if (tool is null)
+                        throw new InvalidOperationException("DotMemory tool is not initialized");
+                    tool.Stop();
+                    tool = null;
+                    break;
+            }
+        }
+
+        public IEnumerable Exporters => Enumerable.Empty();
+        public IEnumerable Analysers => Enumerable.Empty();
+
+        public IEnumerable Validate(ValidationParameters validationParameters)
+        {
+            var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct();
+            foreach (var runtimeMoniker in runtimeMonikers)
+            {
+                if (!IsSupported(runtimeMoniker))
+                    yield return new ValidationError(true, $"Runtime '{runtimeMoniker}' is not supported by dotMemory");
+            }
+        }
+
+        internal static bool IsSupported(RuntimeMoniker runtimeMoniker)
+        {
+            switch (runtimeMoniker)
+            {
+                case RuntimeMoniker.HostProcess:
+                case RuntimeMoniker.Net461:
+                case RuntimeMoniker.Net462:
+                case RuntimeMoniker.Net47:
+                case RuntimeMoniker.Net471:
+                case RuntimeMoniker.Net472:
+                case RuntimeMoniker.Net48:
+                case RuntimeMoniker.Net481:
+                case RuntimeMoniker.Net50:
+                case RuntimeMoniker.Net60:
+                case RuntimeMoniker.Net70:
+                case RuntimeMoniker.Net80:
+                case RuntimeMoniker.Net90:
+                    return true;
+                case RuntimeMoniker.NotRecognized:
+                case RuntimeMoniker.Mono:
+                case RuntimeMoniker.NativeAot60:
+                case RuntimeMoniker.NativeAot70:
+                case RuntimeMoniker.NativeAot80:
+                case RuntimeMoniker.NativeAot90:
+                case RuntimeMoniker.Wasm:
+                case RuntimeMoniker.WasmNet50:
+                case RuntimeMoniker.WasmNet60:
+                case RuntimeMoniker.WasmNet70:
+                case RuntimeMoniker.WasmNet80:
+                case RuntimeMoniker.WasmNet90:
+                case RuntimeMoniker.MonoAOTLLVM:
+                case RuntimeMoniker.MonoAOTLLVMNet60:
+                case RuntimeMoniker.MonoAOTLLVMNet70:
+                case RuntimeMoniker.MonoAOTLLVMNet80:
+                case RuntimeMoniker.MonoAOTLLVMNet90:
+                case RuntimeMoniker.Mono60:
+                case RuntimeMoniker.Mono70:
+                case RuntimeMoniker.Mono80:
+                case RuntimeMoniker.Mono90:
+#pragma warning disable CS0618 // Type or member is obsolete
+                case RuntimeMoniker.NetCoreApp50:
+#pragma warning restore CS0618 // Type or member is obsolete
+                    return false;
+                case RuntimeMoniker.NetCoreApp20:
+                case RuntimeMoniker.NetCoreApp21:
+                case RuntimeMoniker.NetCoreApp22:
+                    return RuntimeInformation.IsWindows();
+                case RuntimeMoniker.NetCoreApp30:
+                case RuntimeMoniker.NetCoreApp31:
+                    return RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux();
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported");
+            }
+        }
+
+        public IEnumerable ProcessResults(DiagnoserResults results) => ImmutableArray.Empty;
+
+        public void DisplayResults(ILogger logger)
+        {
+            if (snapshotFilePaths.Any())
+            {
+                logger.WriteLineInfo("The following dotMemory snapshots were generated:");
+                foreach (string snapshotFilePath in snapshotFilePaths)
+                    logger.WriteLineInfo($"* {snapshotFilePath}");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs
new file mode 100644
index 0000000000..a474312978
--- /dev/null
+++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoserAttribute.cs
@@ -0,0 +1,21 @@
+using System;
+using BenchmarkDotNet.Configs;
+
+namespace BenchmarkDotNet.Diagnostics.dotMemory
+{
+    [AttributeUsage(AttributeTargets.Class)]
+    public class DotMemoryDiagnoserAttribute : Attribute, IConfigSource
+    {
+        public IConfig Config { get; }
+
+        public DotMemoryDiagnoserAttribute()
+        {
+            Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser());
+        }
+
+        public DotMemoryDiagnoserAttribute(Uri? nugetUrl = null, string? toolsDownloadFolder = null)
+        {
+            Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotMemoryDiagnoser(nugetUrl, toolsDownloadFolder));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs
new file mode 100644
index 0000000000..1e640d6708
--- /dev/null
+++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryTool.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.Helpers;
+using BenchmarkDotNet.Loggers;
+using JetBrains.Profiler.SelfApi;
+
+namespace BenchmarkDotNet.Diagnostics.dotMemory
+{
+    internal sealed class DotMemoryTool
+    {
+        private readonly ILogger logger;
+        private readonly Uri? nugetUrl;
+        private readonly NuGetApi nugetApi;
+        private readonly string? downloadTo;
+
+        public DotMemoryTool(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null)
+        {
+            this.logger = logger;
+            this.nugetUrl = nugetUrl;
+            this.nugetApi = nugetApi;
+            this.downloadTo = downloadTo;
+        }
+
+        public void Init()
+        {
+            try
+            {
+                logger.WriteLineInfo("Ensuring that dotMemory prerequisite is installed...");
+                var progress = new Progress(logger, "Installing DotMemory");
+                DotMemory.EnsurePrerequisiteAsync(progress, nugetUrl, nugetApi, downloadTo).Wait();
+                logger.WriteLineInfo("dotMemory prerequisite is installed");
+                logger.WriteLineInfo($"dotMemory runner path: {GetRunnerPath()}");
+            }
+            catch (Exception e)
+            {
+                logger.WriteLineError(e.ToString());
+            }
+        }
+
+        public string Start(DiagnoserActionParameters parameters)
+        {
+            string snapshotFile = ArtifactFileNameHelper.GetFilePath(parameters, "snapshots", DateTime.Now, "dmw", ".0000".Length);
+            string? snapshotDirectory = Path.GetDirectoryName(snapshotFile);
+            logger.WriteLineInfo($"Target snapshot file: {snapshotFile}");
+            if (!Directory.Exists(snapshotDirectory) && snapshotDirectory != null)
+            {
+                try
+                {
+                    Directory.CreateDirectory(snapshotDirectory);
+                }
+                catch (Exception e)
+                {
+                    logger.WriteLineError($"Failed to create directory: {snapshotDirectory}");
+                    logger.WriteLineError(e.ToString());
+                }
+            }
+
+            try
+            {
+                logger.WriteLineInfo("Attaching dotMemory to the process...");
+                Attach(parameters, snapshotFile);
+                logger.WriteLineInfo("dotMemory is successfully attached");
+            }
+            catch (Exception e)
+            {
+                logger.WriteLineError(e.ToString());
+                return snapshotFile;
+            }
+
+            return snapshotFile;
+        }
+
+        public void Stop()
+        {
+            try
+            {
+                logger.WriteLineInfo("Taking dotMemory snapshot...");
+                Snapshot();
+                logger.WriteLineInfo("dotMemory snapshot is successfully taken");
+            }
+            catch (Exception e)
+            {
+                logger.WriteLineError(e.ToString());
+            }
+
+            try
+            {
+                logger.WriteLineInfo("Detaching dotMemory from the process...");
+                Detach();
+                logger.WriteLineInfo("dotMemory is successfully detached");
+            }
+            catch (Exception e)
+            {
+                logger.WriteLineError(e.ToString());
+            }
+        }
+
+        private void Attach(DiagnoserActionParameters parameters, string snapshotFile)
+        {
+            var config = new DotMemory.Config();
+
+            var pid = parameters.Process.Id;
+            var currentPid = Process.GetCurrentProcess().Id;
+            if (pid != currentPid)
+                config = config.ProfileExternalProcess(pid);
+
+            config = config.SaveToFile(snapshotFile);
+            DotMemory.Attach(config);
+        }
+
+        private void Snapshot() => DotMemory.GetSnapshot();
+
+        private void Detach() => DotMemory.Detach();
+
+        private string GetRunnerPath()
+        {
+            var consoleRunnerPackageField = typeof(DotMemory).GetField("ConsoleRunnerPackage", BindingFlags.NonPublic | BindingFlags.Static);
+            if (consoleRunnerPackageField == null)
+                throw new InvalidOperationException("Field 'ConsoleRunnerPackage' not found.");
+
+            object? consoleRunnerPackage = consoleRunnerPackageField.GetValue(null);
+            if (consoleRunnerPackage == null)
+                throw new InvalidOperationException("Unable to get value of 'ConsoleRunnerPackage'.");
+
+            var consoleRunnerPackageType = consoleRunnerPackage.GetType();
+            var getRunnerPathMethod = consoleRunnerPackageType.GetMethod("GetRunnerPath");
+            if (getRunnerPathMethod == null)
+                throw new InvalidOperationException("Method 'GetRunnerPath' not found.");
+
+            string? runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string;
+            if (runnerPath == null)
+                throw new InvalidOperationException("Unable to invoke 'GetRunnerPath'.");
+
+            return runnerPath;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs
new file mode 100644
index 0000000000..738997bb6d
--- /dev/null
+++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/Progress.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Diagnostics;
+using BenchmarkDotNet.Loggers;
+
+namespace BenchmarkDotNet.Diagnostics.dotMemory
+{
+    public class Progress : IProgress
+    {
+        private static readonly TimeSpan ReportInterval = TimeSpan.FromSeconds(0.1);
+
+        private readonly ILogger logger;
+        private readonly string title;
+
+        public Progress(ILogger logger, string title)
+        {
+            this.logger = logger;
+            this.title = title;
+        }
+
+        private int lastProgress;
+        private Stopwatch? stopwatch;
+
+        public void Report(double value)
+        {
+            int progress = (int)Math.Floor(value);
+            bool needToReport = stopwatch == null ||
+                                (stopwatch != null && stopwatch?.Elapsed > ReportInterval) ||
+                                progress == 100;
+
+            if (lastProgress != progress && needToReport)
+            {
+                logger.WriteLineInfo($"{title}: {progress}%");
+                lastProgress = progress;
+                stopwatch = Stopwatch.StartNew();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/Properties/AssemblyInfo.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..270fdc2c9c
--- /dev/null
+++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Runtime.CompilerServices;
+using BenchmarkDotNet.Properties;
+
+[assembly: CLSCompliant(true)]
+
+#if RELEASE
+[assembly: InternalsVisibleTo("BenchmarkDotNet.Tests,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
+#else
+[assembly: InternalsVisibleTo("BenchmarkDotNet.Tests")]
+#endif
\ No newline at end of file
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs
index dfc9903b82..c7f1cf18c8 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs
@@ -70,7 +70,7 @@ protected override void Attach(DiagnoserActionParameters parameters, string snap
             }
 
             if (!attachWaitingTask.Task.Wait(AttachTimeout))
-                throw new Exception($"Failed to attach dotTrace to the target process (timeout: {AttachTimeout.TotalSeconds} sec");
+                throw new Exception($"Failed to attach dotTrace to the target process (timeout: {AttachTimeout.TotalSeconds} sec)");
             if (!attachWaitingTask.Task.Result)
                 throw new Exception($"Failed to attach dotTrace to the target process (ExitCode={process.ExitCode})");
         }
diff --git a/src/BenchmarkDotNet/Properties/AssemblyInfo.cs b/src/BenchmarkDotNet/Properties/AssemblyInfo.cs
index cec4ef220c..ee7077a55e 100644
--- a/src/BenchmarkDotNet/Properties/AssemblyInfo.cs
+++ b/src/BenchmarkDotNet/Properties/AssemblyInfo.cs
@@ -14,6 +14,7 @@
 [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.Windows,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotTrace,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
+[assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotMemory,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.TestAdapter,PublicKey=" + BenchmarkDotNetInfo.PublicKey)]
@@ -22,6 +23,7 @@
 [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests")]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.Windows")]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotTrace")]
+[assembly: InternalsVisibleTo("BenchmarkDotNet.Diagnostics.dotMemory")]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning")]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks")]
 [assembly: InternalsVisibleTo("BenchmarkDotNet.TestAdapter")]
diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj
index 8949cce2c0..a928af1424 100644
--- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj
+++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj
@@ -29,6 +29,7 @@
     
     
     
+    
   
   
     
diff --git a/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs b/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs
new file mode 100644
index 0000000000..1e8dc79d00
--- /dev/null
+++ b/tests/BenchmarkDotNet.IntegrationTests/DotMemoryTests.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Diagnostics.dotMemory;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Portability;
+using BenchmarkDotNet.Toolchains.InProcess.Emit;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace BenchmarkDotNet.IntegrationTests
+{
+    public class DotMemoryTests : BenchmarkTestExecutor
+    {
+        public DotMemoryTests(ITestOutputHelper output) : base(output) { }
+
+        [Fact]
+        public void DotMemorySmokeTest()
+        {
+            if (!RuntimeInformation.IsWindows() && RuntimeInformation.IsMono)
+            {
+                Output.WriteLine("Skip Mono on non-Windows");
+                return;
+            }
+
+            var config = new ManualConfig().AddJob(
+                Job.Dry.WithId("ExternalProcess"),
+                Job.Dry.WithToolchain(InProcessEmitToolchain.Instance).WithId("InProcess")
+            );
+            string snapshotDirectory = Path.Combine(Directory.GetCurrentDirectory(), "BenchmarkDotNet.Artifacts", "snapshots");
+            if (Directory.Exists(snapshotDirectory))
+                Directory.Delete(snapshotDirectory, true);
+
+            CanExecute(config);
+
+            Output.WriteLine("---------------------------------------------");
+            Output.WriteLine("SnapshotDirectory:" + snapshotDirectory);
+            var snapshots = Directory.EnumerateFiles(snapshotDirectory)
+                .Where(filePath => Path.GetExtension(filePath).Equals(".dmw", StringComparison.OrdinalIgnoreCase))
+                .Select(Path.GetFileName)
+                .OrderBy(fileName => fileName)
+                .ToList();
+            Output.WriteLine("Snapshots:");
+            foreach (string snapshot in snapshots)
+                Output.WriteLine("* " + snapshot);
+            Assert.Equal(4, snapshots.Count);
+        }
+
+        [DotMemoryDiagnoser]
+        public class Benchmarks
+        {
+            [Benchmark]
+            public int Foo0()
+            {
+                var list = new List
   
     
+    
     
   
   
diff --git a/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs
new file mode 100644
index 0000000000..7142486280
--- /dev/null
+++ b/tests/BenchmarkDotNet.Tests/dotMemory/DotMemoryTests.cs
@@ -0,0 +1,17 @@
+using System;
+using BenchmarkDotNet.Diagnostics.dotMemory;
+using BenchmarkDotNet.Jobs;
+using Xunit;
+
+namespace BenchmarkDotNet.Tests.dotMemory
+{
+    public class DotMemoryTests
+    {
+        [Fact]
+        public void AllRuntimeMonikerAreKnown()
+        {
+            foreach (RuntimeMoniker moniker in Enum.GetValues(typeof(RuntimeMoniker)))
+                DotMemoryDiagnoser.IsSupported(moniker); // Just check that it doesn't throw exceptions
+        }
+    }
+}
\ No newline at end of file