Skip to content

Commit

Permalink
Log ServiceHub process exit code on failure (#65823)
Browse files Browse the repository at this point in the history
* Report process exit code in gold bar

* Remove unused field
  • Loading branch information
tmat authored Dec 9, 2022
1 parent 8131a70 commit af1e46a
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ internal interface IRemoteProcessTelemetryService
/// Sets <see cref="WorkspaceConfigurationOptions"/> for the process.
/// Called as soon as the remote process is created but can't guarantee that solution entities (projects, documents, syntax trees) have not been created beforehand.
/// </summary>
ValueTask InitializeWorkspaceConfigurationOptionsAsync(WorkspaceConfigurationOptions options, CancellationToken cancellationToken);
/// <returns>Process ID of the remote process.</returns>
ValueTask<int> InitializeAsync(WorkspaceConfigurationOptions options, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ public override RemoteServiceConnection<T> CreateConnection<T>(object? callbackT
_inprocServices.ServiceBrokerClient,
_workspaceServices.GetRequiredService<ISolutionAssetStorageProvider>().AssetStorage,
_workspaceServices.GetRequiredService<IErrorReportingService>(),
shutdownCancellationService: null);
shutdownCancellationService: null,
remoteProcess: null);
}

public override void Dispose()
Expand Down
29 changes: 28 additions & 1 deletion src/Workspaces/Remote/Core/BrokeredServiceConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
Expand All @@ -14,6 +16,7 @@
using Microsoft.CodeAnalysis.Telemetry;
using Microsoft.ServiceHub.Framework;
using Microsoft.VisualStudio.Threading;
using Microsoft.Win32.SafeHandles;
using Roslyn.Utilities;
using StreamJsonRpc;
using StreamJsonRpc.Protocol;
Expand Down Expand Up @@ -49,6 +52,8 @@ public void Dispose()
private readonly ServiceBrokerClient _serviceBrokerClient;
private readonly RemoteServiceCallbackDispatcher.Handle _callbackHandle;
private readonly IRemoteServiceCallbackDispatcher? _callbackDispatcher;
private readonly Process? _remoteProcess;
private readonly SafeProcessHandle? _remoteProcessHandle;

public BrokeredServiceConnection(
ServiceDescriptor serviceDescriptor,
Expand All @@ -57,7 +62,8 @@ public BrokeredServiceConnection(
ServiceBrokerClient serviceBrokerClient,
SolutionAssetStorage solutionAssetStorage,
IErrorReportingService? errorReportingService,
IRemoteHostClientShutdownCancellationService? shutdownCancellationService)
IRemoteHostClientShutdownCancellationService? shutdownCancellationService,
Process? remoteProcess)
{
Contract.ThrowIfFalse((callbackDispatcher == null) == (serviceDescriptor.ClientInterface == null));

Expand All @@ -68,6 +74,8 @@ public BrokeredServiceConnection(
_shutdownCancellationService = shutdownCancellationService;
_callbackDispatcher = callbackDispatcher;
_callbackHandle = callbackDispatcher?.CreateHandle(callbackTarget) ?? default;
_remoteProcess = remoteProcess;
_remoteProcessHandle = _remoteProcess?.SafeHandle;
}

public override void Dispose()
Expand Down Expand Up @@ -443,7 +451,26 @@ private void OnUnexpectedException(Exception exception, CancellationToken cancel
internalException = exception;
}

try
{
// Process.ExitCode throws "Process was not started by this object, so requested information cannot be determined.",
// Use Win32 API directly.
if (_remoteProcess?.HasExited == true && NativeMethods.GetExitCodeProcess(_remoteProcessHandle!, out var exitCode))
{
message += $" Exit code {exitCode}";
}
}
catch
{
}

_errorReportingService.ShowFeatureNotAvailableErrorInfo(message, TelemetryFeatureName.GetRemoteFeatureName(_serviceDescriptor.ComponentName, _serviceDescriptor.SimpleName), internalException);
}
}

internal static partial class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetExitCodeProcess(SafeProcessHandle processHandle, out int exitCode);
}
}
30 changes: 24 additions & 6 deletions src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading;
Expand Down Expand Up @@ -36,6 +37,8 @@ internal sealed partial class ServiceHubRemoteHostClient : RemoteHostClient

public readonly RemoteProcessConfiguration Configuration;

private Process? _remoteProcess;

private ServiceHubRemoteHostClient(
SolutionServices services,
RemoteProcessConfiguration configuration,
Expand Down Expand Up @@ -76,12 +79,26 @@ public static async Task<RemoteHostClient> CreateAsync(

var client = new ServiceHubRemoteHostClient(services, configuration, serviceBrokerClient, hubClient, callbackDispatchers);

var workspaceConfigurationService = services.GetService<IWorkspaceConfigurationService>();
if (workspaceConfigurationService != null)
var workspaceConfigurationService = services.GetRequiredService<IWorkspaceConfigurationService>();

var remoteProcessId = await client.TryInvokeAsync<IRemoteProcessTelemetryService, int>(
(service, cancellationToken) => service.InitializeAsync(workspaceConfigurationService.Options, cancellationToken),
cancellationToken).ConfigureAwait(false);

if (remoteProcessId.HasValue)
{
await client.TryInvokeAsync<IRemoteProcessTelemetryService>(
(service, cancellationToken) => service.InitializeWorkspaceConfigurationOptionsAsync(workspaceConfigurationService.Options, cancellationToken),
cancellationToken).ConfigureAwait(false);
try
{
client._remoteProcess = Process.GetProcessById(remoteProcessId.Value);
}
catch (Exception e)
{
hubClient.Logger.TraceEvent(TraceEventType.Error, 1, $"Unable to find Roslyn ServiceHub process: {e.Message}");
}
}
else
{
hubClient.Logger.TraceEvent(TraceEventType.Error, 1, "Roslyn ServiceHub process initialization failed.");
}

if (configuration.HasFlag(RemoteProcessConfiguration.EnableSolutionCrawler))
Expand Down Expand Up @@ -121,7 +138,8 @@ internal RemoteServiceConnection<T> CreateConnection<T>(ServiceDescriptors descr
_serviceBrokerClient,
_assetStorage,
_errorReportingService,
_shutdownCancellationService);
_shutdownCancellationService,
_remoteProcess);
}

public override void Dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -112,14 +113,14 @@ private static void SetRoslynLogger<T>(ImmutableArray<string> loggerTypes, Func<
/// <summary>
/// Remote API.
/// </summary>
public ValueTask InitializeWorkspaceConfigurationOptionsAsync(WorkspaceConfigurationOptions options, CancellationToken cancellationToken)
public ValueTask<int> InitializeAsync(WorkspaceConfigurationOptions options, CancellationToken cancellationToken)
{
return RunServiceAsync(cancellationToken =>
{
var service = (RemoteWorkspaceConfigurationService)GetWorkspaceServices().GetRequiredService<IWorkspaceConfigurationService>();
service.InitializeOptions(options);

return ValueTaskFactory.CompletedTask;
return ValueTaskFactory.FromResult(Process.GetCurrentProcess().Id);
}, cancellationToken);
}
}
Expand Down

0 comments on commit af1e46a

Please sign in to comment.