From 8c8fd6446be485a9a1a471c7ad61ca5d0e426d1c Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 27 May 2016 16:01:36 -0700 Subject: [PATCH] Use regular PDBs on full desktop when possible Add a test to verify exceptions thrown from views is pretty printed by diagnostics middleware Fixes https://github.com/aspnet/Diagnostics/issues/293 Fixes #4737 --- .../DefaultRoslynCompilationService.cs | 5 +- .../Internal/SymbolsUtility.cs | 45 ++++++++++++++++++ .../ErrorPageTests.cs | 47 ++++++++++++++----- .../ErrorPageMiddlewareController.cs | 3 ++ .../ErrorPageMiddleware/RuntimeError.cshtml | 3 ++ 5 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Mvc.Razor/Internal/SymbolsUtility.cs create mode 100644 test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorPageMiddleware/RuntimeError.cshtml diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs index bc7701734f..ec074069d2 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs @@ -27,6 +27,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal /// public class DefaultRoslynCompilationService : ICompilationService { + private readonly DebugInformationFormat _pdbFormat = SymbolsUtility.SupportsFullPdbGeneration() ? + DebugInformationFormat.Pdb : + DebugInformationFormat.PortablePdb; private readonly ApplicationPartManager _partManager; private readonly IFileProvider _fileProvider; private readonly Action _compilationCallback; @@ -116,7 +119,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal var result = compilation.Emit( assemblyStream, pdbStream, - options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); + options: new EmitOptions(debugInformationFormat: _pdbFormat)); if (!result.Success) { diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/SymbolsUtility.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/SymbolsUtility.cs new file mode 100644 index 0000000000..f7889a2b3b --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/SymbolsUtility.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Mvc.Razor.Internal +{ + /// + /// Utility type for determining if a platform supports full pdb file generation. + /// + public class SymbolsUtility + { + private const string SymWriterGuid = "0AE2DEB0-F901-478b-BB9F-881EE8066788"; + + /// + /// Determines if the current platform supports full pdb generation. + /// + /// true if full pdb generation is supported; false otherwise. + public static bool SupportsFullPdbGeneration() + { + if (Type.GetType("Mono.Runtime") != null) + { + return false; + } + + try + { + // Check for the pdb writer component that roslyn uses to generate pdbs + var type = Marshal.GetTypeFromCLSID(new Guid(SymWriterGuid)); + if (type != null) + { + // This line will throw if pdb generation is not supported. + Activator.CreateInstance(type); + return true; + } + } + catch + { + } + + return false; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs index aabb9db844..818e2082bb 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ErrorPageTests.cs @@ -4,10 +4,9 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Text.Encodings.Web; using System.Threading.Tasks; -#if !NETCOREAPP1_0 -using Microsoft.AspNetCore.Testing.xunit; -#endif +using Microsoft.AspNetCore.Mvc.Razor.Internal; using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests @@ -24,17 +23,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public HttpClient Client { get; } -#if NETCOREAPP1_0 [Theory] -#else - [ConditionalTheory] - [OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "aspnet/Mvc#3587")] -#endif [InlineData("CompilationFailure", "Cannot implicitly convert type 'int' to 'string'")] - [InlineData("ParserError", "The code block is missing a closing "}" character. Make sure you " + - "have a matching "}" character for all the "{" characters " + - "within this block, and that none of the "}" characters are being " + - "interpreted as markup.")] + [InlineData("ParserError", + "The code block is missing a closing "}" character. Make sure you " + + "have a matching "}" character for all the "{" characters " + + "within this block, and that none of the "}" characters are being " + + "interpreted as markup.")] public async Task CompilationFailuresAreListedByErrorPageMiddleware(string action, string expected) { // Arrange @@ -69,5 +64,33 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Contains("/Views/ErrorFromViewImports/_ViewImports.cshtml", content); Assert.Contains(expectedMessage, content); } + + [Fact] + public async Task RuntimeErrorAreListedByErrorPageMiddleware() + { + // The desktop CLR does not correctly read the stack trace from portable PDBs. However generating full pdbs + // is only supported on machines with CLSID_CorSymWriter available. On desktop, we'll skip this test on + // machines without this component. +#if NET451 + if (!SymbolsUtility.SupportsFullPdbGeneration()) + { + return; + } +#endif + + // Arrange + var expectedMessage = HtmlEncoder.Default.Encode("throw new Exception(\"Error from view\");"); + var expectedMediaType = MediaTypeHeaderValue.Parse("text/html; charset=utf-8"); + + // Act + var response = await Client.GetAsync("http://localhost/RuntimeError"); + + // Assert + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + Assert.Equal(expectedMediaType, response.Content.Headers.ContentType); + var content = await response.Content.ReadAsStringAsync(); + Assert.Contains("/Views/ErrorPageMiddleware/RuntimeError.cshtml", content); + Assert.Contains(expectedMessage, content); + } } } \ No newline at end of file diff --git a/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs b/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs index 39e2c65d45..68f3b451f5 100644 --- a/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs +++ b/test/WebSites/ErrorPageMiddlewareWebSite/ErrorPageMiddlewareController.cs @@ -24,5 +24,8 @@ namespace ErrorPageMiddlewareWebSite { return View("~/Views/ErrorFromViewImports/Index.cshtml"); } + + [HttpGet("/RuntimeError")] + public IActionResult RuntimeError() => View(); } } diff --git a/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorPageMiddleware/RuntimeError.cshtml b/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorPageMiddleware/RuntimeError.cshtml new file mode 100644 index 0000000000..d476445cdb --- /dev/null +++ b/test/WebSites/ErrorPageMiddlewareWebSite/Views/ErrorPageMiddleware/RuntimeError.cshtml @@ -0,0 +1,3 @@ +@{ + throw new Exception("Error from view"); +} \ No newline at end of file