Skip to content

Commit

Permalink
Adding memorydump proxy API
Browse files Browse the repository at this point in the history
  • Loading branch information
puneetg1983 committed May 17, 2021
1 parent d4bb543 commit c262b0f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 6 deletions.
1 change: 1 addition & 0 deletions Kudu.Services.Web/Kudu.Services.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Message Importance="High" Text="%0a+++ Building KuduLite +++%0a%0a" />
</Target>
<ItemGroup>
<PackageReference Include="AspNetCore.Proxy" Version="4.2.0" />
<PackageReference Include="AspNetCore.RouteAnalyzer" Version="0.5.3" />
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Microsoft.AspNetCore.App" />
Expand Down
4 changes: 4 additions & 0 deletions Kudu.Services.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
using Newtonsoft.Json.Serialization;
using Swashbuckle.AspNetCore.Swagger;
using ILogger = Kudu.Core.Deployment.ILogger;
using AspNetCore.Proxy;

namespace Kudu.Services.Web
{
Expand Down Expand Up @@ -277,6 +278,9 @@ public void ConfigureServices(IServiceCollection services)

services.AddScoped<ICommandExecutor, CommandExecutor>();

// Required for proxying requests to dotnet-monitor
services.AddProxies();

// KuduWebUtil.MigrateSite(environment, noContextDeploymentsSettingsManager);
// RemoveOldTracePath(environment);
// RemoveTempFileFromUserDrive(environment);
Expand Down
125 changes: 119 additions & 6 deletions Kudu.Services/Diagnostics/LinuxProcessController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using AspNetCore.Proxy;
using Kudu.Contracts.Diagnostics;
using Kudu.Services.Arm;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;

namespace Kudu.Services.Performance
{
Expand All @@ -15,7 +19,18 @@ namespace Kudu.Services.Performance

public class LinuxProcessController : Controller
{
const string dotnetMonitorPort = "50051";
const string DotNetMonitorAddressCacheKey = "DotNetMonitorAddressCacheKey";

private IMemoryCache _cache;

public LinuxProcessController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}

private const string ERRORMSG = "Not supported on Linux";
private const string DOTNETMONITORSTOPPED = "The dotnet-monitor tool is not running";

[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Parameters preserved for equivalent route binding")]
[HttpGet]
Expand Down Expand Up @@ -51,18 +66,46 @@ public IActionResult GetAllModules(int id)

[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Parameters preserved for equivalent route binding")]
[HttpGet]
public IActionResult GetAllProcesses(bool allUsers = false)
public Task GetAllProcesses(bool allUsers = false)
{
if (IsDotNetMonitorEnabled())
{
var dotnetMonitorAddress = GetDotNetMonitorAddress();
if (!string.IsNullOrWhiteSpace(dotnetMonitorAddress))
{
return this.HttpProxyAsync($"{dotnetMonitorAddress}/processes");
}
else
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return Task.FromResult(Response.Body.WriteAsync(Encoding.UTF8.GetBytes(DOTNETMONITORSTOPPED)));
}
}

Response.StatusCode = (int)HttpStatusCode.BadRequest;
return new JsonResult(ERRORMSG);
return Task.FromResult(Response.Body.WriteAsync(Encoding.UTF8.GetBytes(ERRORMSG)));
}

[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Parameters preserved for equivalent route binding")]
[HttpGet]
public IActionResult GetProcess(int id)
public Task GetProcess(int id)
{
if (IsDotNetMonitorEnabled())
{
var dotnetMonitorAddress = GetDotNetMonitorAddress();
if (!string.IsNullOrWhiteSpace(dotnetMonitorAddress))
{
return this.HttpProxyAsync($"{dotnetMonitorAddress}/processes/{id}");
}
else
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return Task.FromResult(Response.Body.WriteAsync(Encoding.UTF8.GetBytes(DOTNETMONITORSTOPPED)));
}
}

Response.StatusCode = (int)HttpStatusCode.BadRequest;
return new JsonResult(ERRORMSG);
return Task.FromResult(Response.Body.WriteAsync(Encoding.UTF8.GetBytes(ERRORMSG)));
}

[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Parameters preserved for equivalent route binding")]
Expand All @@ -75,10 +118,23 @@ public IActionResult KillProcess(int id)

[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Parameters preserved for equivalent route binding")]
[HttpGet]
public IActionResult MiniDump(int id, int dumpType = 0, string format = null)
public Task MiniDump(int id, string type = "WithHeap")
{
if (IsDotNetMonitorEnabled())
{
var dotnetMonitorAddress = GetDotNetMonitorAddress();
if (!string.IsNullOrWhiteSpace(dotnetMonitorAddress))
{
return this.HttpProxyAsync($"{dotnetMonitorAddress}/dump/{id}?type={type}");
}

Response.StatusCode = (int)HttpStatusCode.NotFound;
return Task.FromResult(Response.Body.WriteAsync(Encoding.UTF8.GetBytes(DOTNETMONITORSTOPPED)));

}

Response.StatusCode = (int)HttpStatusCode.BadRequest;
return new JsonResult(ERRORMSG);
return Task.FromResult(Response.Body.WriteAsync(Encoding.UTF8.GetBytes(ERRORMSG)));
}

[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Parameters preserved for equivalent route binding")]
Expand Down Expand Up @@ -107,5 +163,62 @@ public IActionResult GetEnvironments(int id, string filter)

return Ok(ArmUtils.AddEnvelopeOnArmRequest(new ProcessEnvironmentInfo(filter, envs), Request));
}

private string GetDotNetMonitorAddress()
{
var dotnetMonitorAddress = _cache.GetOrCreate(DotNetMonitorAddressCacheKey, entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2);
var ipAddress = GetIpAddress();
if (!string.IsNullOrWhiteSpace(ipAddress))
{
return $"http://{ipAddress}:{dotnetMonitorPort}";
}
return string.Empty;
});

return dotnetMonitorAddress;
}

private string GetIpAddress()
{
try
{
string ipAddress = System.IO.File.ReadAllText("/appsvctmp/ipaddr_" + Environment.GetEnvironmentVariable("WEBSITE_ROLE_INSTANCE_ID"));
if (ipAddress != null)
{
if (ipAddress.Contains(':'))
{
string[] ipAddrPortStr = ipAddress.Split(":");
return ipAddrPortStr[0];
}
else
{
return ipAddress;
}
}
}
catch (Exception)
{
}
return string.Empty;
}

private bool IsDotNetMonitorEnabled()
{
string val = Environment.GetEnvironmentVariable("WEBSITE_USE_DOTNET_MONITOR");
if (!string.IsNullOrWhiteSpace(val))
{
string stack = Environment.GetEnvironmentVariable("WEBSITE_STACK");

if (!string.IsNullOrWhiteSpace(stack))
{
return val.Equals("true", StringComparison.OrdinalIgnoreCase)
&& stack.Equals("DOTNETCORE", StringComparison.OrdinalIgnoreCase);
}
}

return false;
}
}
}
1 change: 1 addition & 0 deletions Kudu.Services/Kudu.Services.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ServerGarbageCollection>false</ServerGarbageCollection>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.Proxy" Version="4.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="2.2.0" />
</ItemGroup>
Expand Down

0 comments on commit c262b0f

Please sign in to comment.