Skip to content

Commit

Permalink
fix: get string length for .NET Framework dump
Browse files Browse the repository at this point in the history
  • Loading branch information
Ne4to committed Jan 31, 2024
1 parent 311c915 commit 01ec0f4
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 32 deletions.
2 changes: 1 addition & 1 deletion src/Heartbeat.Runtime/Analyzers/LongStringAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public IReadOnlyCollection<LongStringInfo> GetStrings(ObjectGCStatus? status, in
foreach (var stringClrObject in GetLongestStrings(count, status))
{
string value = stringClrObject.AsString(truncateLength ?? 4096)!;
var length = stringClrObject.ReadField<int>("_stringLength");
var length = stringClrObject.ReadField<int>(Context.GetStringLengthFieldName());
var size = new Size(stringClrObject.Size);
result.Add(new LongStringInfo(new(stringClrObject.Address), size, length, value));
}
Expand Down
2 changes: 1 addition & 1 deletion src/Heartbeat.Runtime/Analyzers/StringDuplicateAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ from clrObject in Context.EnumerateStrings(ObjectGcStatus, Generation)
}
else
{
var fullLength = stringInstance.ReadField<int>("_stringLength");
var fullLength = stringInstance.ReadField<int>(Context.GetStringLengthFieldName());
stringCount[stringValue] = new StringDuplicateInfo(1, fullLength);
}
}
Expand Down
14 changes: 10 additions & 4 deletions src/Heartbeat.Runtime/RuntimeContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

using Heartbeat.Runtime.Domain;
using Heartbeat.Runtime.Extensions;
using Heartbeat.Runtime.Models;
Expand Down Expand Up @@ -45,6 +44,13 @@ public string GetAutoPropertyFieldName(string propertyName)
throw new NotImplementedException();
}

public string GetStringLengthFieldName()
{
return IsCoreRuntime
? "_stringLength"
: "m_stringLength";
}

public IEnumerable<ulong> EnumerateObjectAddressesByTypeName(string typeName, ObjectGCStatus? status)
{
var clrType = Heap.GetTypeByName(typeName);
Expand Down Expand Up @@ -74,7 +80,7 @@ where obj.IsValid
}

public IEnumerable<ClrObject> EnumerateObjectsByTypeName(
string typeName,
string typeName,
ObjectGCStatus? status,
Generation? generation = null)
{
Expand Down Expand Up @@ -114,7 +120,7 @@ from clrObject in Heap.EnumerateObjects()
where type != null && !type.IsFree && type.Name == "System.Threading.Thread"
let managedThreadId = type.GetFieldByName("m_ManagedThreadId")!.Read<int>(clrObject, true)
let threadName = type.GetFieldByName("m_Name")!.ReadString(clrObject, false)
select new {managedThreadId, threadName};
select new { managedThreadId, threadName };

// // <{0}>k__BackingField
// var threadQuery = from address in heap.EnumerateObjectAddresses()
Expand Down Expand Up @@ -195,7 +201,7 @@ public ulong GetWeakRefValue(ClrObject weakRefObject)
var intPtrValueField = intPtrType.GetFieldByName(valueField);

var handleAddr = weakRefHandleField.Read<long>(weakRefObject.Address, true);
var value = intPtrValueField.Read<ulong>((ulong) handleAddr, true);
var value = intPtrValueField.Read<ulong>((ulong)handleAddr, true);

return value;
}
Expand Down
5 changes: 4 additions & 1 deletion src/Heartbeat/ClientApp/src/pages/clrObject/ClrObject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import Box from "@mui/material/Box";
// TODO add Dictionary, Queue, Stack and other collections view to a new tab
// TODO add ConcurrentDictionary view to a new tab (dcd, dumpconcurrentdictionary <address> Displays concurrent dictionary content.)
// TODO add ConcurrentQueue view to a new tab (dcq, dumpconcurrentqueue <address> Displays concurrent queue content.)
// TODO add JWT decode tab (https://github.com/panva/jose) (System.IdentityModel.Tokens.Jwt)
// TODO add base64 decode tab (base64 string -> utf8 string) | devenv dump - https://localhost:44443/#/clr-object/0000021d1b6452b8/show
// TODO add gzip string decode tab | devenv dump - https://localhost:44443/#/clr-object/0000021d1b6f6f80/show
// TODO add certificate decode tab
// TODO find other debugger visualizers

export const ClrObject = () => {
Expand Down Expand Up @@ -92,6 +94,7 @@ export const ClrObject = () => {
{title: 'Size', value: toSizeString(objectResult.size || 0)},
{title: 'Generation', value: objectResult.generation},
// TODO add Live / Dead
// TODO add ValueType / reference type
{title: 'MethodTable', value: renderMethodTableLink(objectResult.methodTable)},
{title: 'Type', value: objectResult.typeName},
{title: 'Module', value: objectResult.moduleName},
Expand Down
2 changes: 1 addition & 1 deletion src/Heartbeat/Endpoints/RouteHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public static IEnumerable<StringInfo> GetStrings(
{
var query = from obj in context.EnumerateStrings(gcStatus, generation)
let str = obj.AsString()
let length = obj.ReadField<int>("_stringLength")
let length = obj.ReadField<int>(context.GetStringLengthFieldName())
select new StringInfo(obj.Address, length, obj.Size, str);

// TODO limit output qty
Expand Down
48 changes: 24 additions & 24 deletions src/Heartbeat/Helpers/JwtParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,8 @@ internal static class JwtParser
["vp"] = "Verifiable Presentation as specified in the W3C Recommendation",
["sph"] = "SIP Priority header field",
["ace_profile"] = "The ACE profile a token is supposed to be used with.",
["cnonce"] =
"\"client-nonce\". A nonce previously provided to the AS by the RS via the client. Used to verify token freshness when the RS cannot synchronize its clock with the AS.",
["exi"] =
"\"Expires in\". Lifetime of the token in seconds from the time the RS first sees it. Used to implement a weaker from of token expiration for devices that cannot synchronize their internal clocks.",
["cnonce"] = "\"client-nonce\". A nonce previously provided to the AS by the RS via the client. Used to verify token freshness when the RS cannot synchronize its clock with the AS.",
["exi"] = "\"Expires in\". Lifetime of the token in seconds from the time the RS first sees it. Used to implement a weaker from of token expiration for devices that cannot synchronize their internal clocks.",
["roles"] = "Roles",
["groups"] = "Groups",
["entitlements"] = "Entitlements",
Expand Down Expand Up @@ -133,23 +131,17 @@ internal static class JwtParser
["cdnistt"] = "CDNI Signed Token Transport Method for Signed Token Renewal",
["cdnistd"] = "CDNI Signed Token Depth",
["sig_val_claims"] = "Signature Validation Token",
["authorization_details"] =
"The claim authorization_details contains a JSON array of JSON objects representing the rights of the access token. Each JSON object contains the data to specify the authorization requirements for a certain type of resource.",
["verified_claims"] =
"This container Claim is composed of the verification evidence related to a certain verification process and the corresponding Claims about the End-User which were verified in this process.",
["authorization_details"] = "The claim authorization_details contains a JSON array of JSON objects representing the rights of the access token. Each JSON object contains the data to specify the authorization requirements for a certain type of resource.",
["verified_claims"] = "This container Claim is composed of the verification evidence related to a certain verification process and the corresponding Claims about the End-User which were verified in this process.",
["place_of_birth"] = "A structured Claim representing the End-User's place of birth.",
["nationalities"] = "String array representing the End-User's nationalities.",
["birth_family_name"] =
"Family name(s) someone has when they were born, or at least from the time they were a child. This term can be used by a person who changes the family name(s) later in life for any reason. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.",
["birth_given_name"] =
"Given name(s) someone has when they were born, or at least from the time they were a child. This term can be used by a person who changes the given name later in life for any reason. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.",
["birth_middle_name"] =
"Middle name(s) someone has when they were born, or at least from the time they were a child. This term can be used by a person who changes the middle name later in life for any reason. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.",
["birth_family_name"] = "Family name(s) someone has when they were born, or at least from the time they were a child. This term can be used by a person who changes the family name(s) later in life for any reason. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.",
["birth_given_name"] = "Given name(s) someone has when they were born, or at least from the time they were a child. This term can be used by a person who changes the given name later in life for any reason. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.",
["birth_middle_name"] = "Middle name(s) someone has when they were born, or at least from the time they were a child. This term can be used by a person who changes the middle name later in life for any reason. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.",
["salutation"] = "End-User's salutation, e.g., \"Mr.\"",
["title"] = "End-User's title, e.g., \"Dr.\"",
["msisdn"] = "End-User's mobile phone number formatted according to ITU-T recommendation [E.164]",
["also_known_as"] =
"Stage name, religious name or any other type of alias/pseudonym with which a person is known in a specific context besides its legal name. This must be part of the applicable legislation and thus the trust framework (e.g., be an attribute on the identity card).",
["also_known_as"] = "Stage name, religious name or any other type of alias/pseudonym with which a person is known in a specific context besides its legal name. This must be part of the applicable legislation and thus the trust framework (e.g., be an attribute on the identity card).",
["htm"] = "The HTTP method of the request",
["htu"] = "The HTTP URI of the request (without query and fragment parts)",
["ath"] = "The base64url-encoded SHA-256 hash of the ASCII encoding of the associated access token's value",
Expand All @@ -161,10 +153,8 @@ internal static class JwtParser
["msgi"] = "Message Integrity Information",
["_claim_names"] = "JSON object whose member names are the Claim Names for the Aggregated and Distributed Claims",
["_claim_sources"] = "JSON object whose member names are referenced by the member values of the _claim_names member",
["rdap_allowed_purposes"] =
"This claim describes the set of RDAP query purposes that are available to an identity that is presented for access to a protected RDAP resource.",
["rdap_dnt_allowed"] =
"This claim contains a JSON boolean literal that describes a \"do not track\" request for server-side tracking, logging, or recording of an identity that is presented for access to a protected RDAP resource.",
["rdap_allowed_purposes"] = "This claim describes the set of RDAP query purposes that are available to an identity that is presented for access to a protected RDAP resource.",
["rdap_dnt_allowed"] = "This claim contains a JSON boolean literal that describes a \"do not track\" request for server-side tracking, logging, or recording of an identity that is presented for access to a protected RDAP resource.",
}.ToFrozenDictionary();

public static JwtInfo ToJwtInfo(string str)
Expand All @@ -183,18 +173,28 @@ public static JwtInfo ToJwtInfo(string str)
var headerValues = header
.Select(kvp => new JwtValue(kvp.Key, kvp.Value, _headerKeyDescription.GetValueOrDefault(kvp.Key)))
.ToArray();

var payloadValues = payload
.Select(kvp => new JwtValue(
kvp.Key,
_numericDateClaimKeys.Contains(kvp.Key)
? $"{kvp.Value} ({DateTimeOffset.FromUnixTimeSeconds((long)(decimal)kvp.Value):R})"
: kvp.Value.ToString()!,
GetPayloadValue(kvp),
_payloadKeyDescription.GetValueOrDefault(kvp.Key)))
.ToArray();
var result = new JwtInfo(headerValues, payloadValues);
return result;
}

private static string GetPayloadValue(KeyValuePair<string, object> kvp)
{
if (kvp.Value is List<object> list)
{
return $"[{string.Join(',', list)}]";
}

return _numericDateClaimKeys.Contains(kvp.Key)
? $"{kvp.Value} ({DateTimeOffset.FromUnixTimeSeconds((long)(decimal)kvp.Value):R})"
: kvp.Value.ToString()!;
}
}

[JsonSerializable(typeof(JWT.Builder.JwtHeader))]
Expand Down

0 comments on commit 01ec0f4

Please sign in to comment.