Skip to content

Commit c65ec17

Browse files
Type check enablement (#383)
* Method for full signature type parsing * update type tests for sample library
1 parent 387f85c commit c65ec17

File tree

9 files changed

+611
-122
lines changed

9 files changed

+611
-122
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Samples.ExampleLibrary.FakeClient
5+
{
6+
public class Biscuit<T> : Biscuit
7+
{
8+
public T Reward { get; set; }
9+
}
10+
11+
public class Biscuit
12+
{
13+
public Guid Id { get; set; }
14+
public string Message { get; set; }
15+
public List<object> Treats { get; set; } = new List<object>();
16+
}
17+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
5+
namespace Samples.ExampleLibrary.FakeClient
6+
{
7+
public class DogClient<T1, T2>
8+
{
9+
public void Silence()
10+
{
11+
Task.Delay(1).Wait();
12+
}
13+
14+
public void Sit(
15+
string message,
16+
int howManyTimes,
17+
byte[] whatEvenIs = null,
18+
Guid[][] whatEvenIsThis = null,
19+
T1[][][] whatEvenIsThisT = null,
20+
List<byte[][]> evenMoreWhatIsThis = null,
21+
List<DogTrick<T1>> previousTricks = null,
22+
Tuple<int, T1, string, object, Tuple<Tuple<T2, long>, long>, Task, Guid> tuple = null,
23+
Dictionary<int, IList<Task<DogTrick<T1>>>> whatAmIDoing = null)
24+
{
25+
for (var i = 0; i < howManyTimes; i++)
26+
{
27+
message +=
28+
message
29+
+ whatEvenIs?.ToString()
30+
+ whatEvenIsThis?.ToString()
31+
+ whatEvenIsThisT?.ToString()
32+
+ evenMoreWhatIsThis?.GetType()
33+
+ previousTricks?.GetType()
34+
+ tuple?.GetType()
35+
+ whatAmIDoing?.GetType();
36+
}
37+
}
38+
39+
public Biscuit Rollover(Guid clientId, short timesToRun, DogTrick trick)
40+
{
41+
var biscuit = new Biscuit
42+
{
43+
Id = clientId,
44+
Message = trick.Message
45+
};
46+
47+
Sit("Sit!", timesToRun);
48+
49+
return biscuit;
50+
}
51+
52+
public async Task<Biscuit<T1>> StayAndLayDown<TM1, TM2>(Guid clientId, short timesToRun, DogTrick<T1> trick, TM1 extraTreat, TM2 extraExtraTreat)
53+
{
54+
await Task.Delay(5);
55+
var biscuit = new Biscuit<T1>();
56+
biscuit.Treats.Add(extraTreat);
57+
biscuit.Treats.Add(extraExtraTreat);
58+
return await Task.FromResult(biscuit);
59+
}
60+
}
61+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Samples.ExampleLibrary.FakeClient
2+
{
3+
public class DogTrick<T>
4+
{
5+
public string Message { get; set; }
6+
public T Reward { get; set; }
7+
}
8+
9+
public class DogTrick
10+
{
11+
public string Message { get; set; }
12+
}
13+
}

src/Datadog.Trace.ClrProfiler.Native/clr_helpers.cpp

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <cstring>
44

5+
#include <set>
6+
#include <stack>
57
#include "environment_variables.h"
68
#include "logging.h"
79
#include "macros.h"
@@ -386,4 +388,255 @@ bool DisableOptimizations() {
386388
return true;
387389
#endif
388390
}
391+
392+
TypeInfo RetrieveTypeForSignature(
393+
const ComPtr<IMetaDataImport2>& metadata_import,
394+
const FunctionInfo& function_info, const int& current_index,
395+
ULONG& token_length) {
396+
mdToken type_token;
397+
const auto type_token_start =
398+
PCCOR_SIGNATURE(&function_info.signature.data[current_index]);
399+
token_length = CorSigUncompressToken(type_token_start, &type_token);
400+
auto type_data = GetTypeInfo(metadata_import, type_token);
401+
return type_data;
402+
}
403+
404+
bool SignatureFuzzyMatch(const ComPtr<IMetaDataImport2>& metadata_import,
405+
const FunctionInfo& function_info,
406+
std::vector<WSTRING>& signature_result) {
407+
const int signature_size = function_info.signature.data.size();
408+
auto generic_count = function_info.signature.NumberOfTypeArguments();
409+
auto param_count = function_info.signature.NumberOfArguments();
410+
auto current_index = 2; // Where the parameters actually start
411+
412+
if (generic_count > 0) {
413+
current_index++; // offset by one because the method is generic
414+
}
415+
416+
const UINT expected_number_of_types = param_count + 1;
417+
UINT current_type_index = 0;
418+
std::vector<WSTRING> type_names(expected_number_of_types);
419+
420+
std::stack<int> generic_arg_stack;
421+
WSTRING append_to_type = ""_W;
422+
WSTRING current_type_name = ""_W;
423+
424+
for (; current_index < signature_size; current_index++) {
425+
mdToken type_token;
426+
ULONG token_length;
427+
auto param_piece = function_info.signature.data[current_index];
428+
const auto cor_element_type = CorElementType(param_piece);
429+
430+
switch (cor_element_type) {
431+
case ELEMENT_TYPE_VOID: {
432+
current_type_name.append("System.Void"_W);
433+
break;
434+
}
435+
436+
case ELEMENT_TYPE_BOOLEAN: {
437+
current_type_name.append("System.Boolean"_W);
438+
break;
439+
}
440+
441+
case ELEMENT_TYPE_CHAR: {
442+
current_type_name.append("System.Char16"_W);
443+
break;
444+
}
445+
446+
case ELEMENT_TYPE_I1: {
447+
current_type_name.append("System.Int8"_W);
448+
break;
449+
}
450+
451+
case ELEMENT_TYPE_U1: {
452+
current_type_name.append("System.UInt8"_W);
453+
break;
454+
}
455+
456+
case ELEMENT_TYPE_I2: {
457+
current_type_name.append("System.Int16"_W);
458+
break;
459+
}
460+
461+
case ELEMENT_TYPE_U2: {
462+
current_type_name.append("System.UInt16"_W);
463+
break;
464+
}
465+
466+
case ELEMENT_TYPE_I4: {
467+
current_type_name.append("System.Int32"_W);
468+
break;
469+
}
470+
471+
case ELEMENT_TYPE_U4: {
472+
current_type_name.append("System.UInt32"_W);
473+
break;
474+
}
475+
476+
case ELEMENT_TYPE_I8: {
477+
current_type_name.append("System.Int64"_W);
478+
break;
479+
}
480+
481+
case ELEMENT_TYPE_U8: {
482+
current_type_name.append("System.UInt64"_W);
483+
break;
484+
}
485+
486+
case ELEMENT_TYPE_R4: {
487+
current_type_name.append("System.Single"_W);
488+
break;
489+
}
490+
491+
case ELEMENT_TYPE_R8: {
492+
current_type_name.append("System.Double"_W);
493+
break;
494+
}
495+
496+
case ELEMENT_TYPE_STRING: {
497+
current_type_name.append("System.String"_W);
498+
break;
499+
}
500+
501+
case ELEMENT_TYPE_OBJECT: {
502+
current_type_name.append("System.Object"_W);
503+
break;
504+
}
505+
506+
case ELEMENT_TYPE_VALUETYPE:
507+
case ELEMENT_TYPE_CLASS: {
508+
current_index++;
509+
auto type_data = RetrieveTypeForSignature(
510+
metadata_import, function_info, current_index, token_length);
511+
// index will be moved up one on every loop
512+
// handle tokens which have more than one byte
513+
current_index += token_length - 1;
514+
current_type_name.append(type_data.name);
515+
break;
516+
}
517+
518+
case ELEMENT_TYPE_SZARRAY: {
519+
append_to_type.append("[]"_W);
520+
while (
521+
CorElementType(function_info.signature.data[current_index + 1]) ==
522+
ELEMENT_TYPE_SZARRAY) {
523+
append_to_type.append("[]"_W);
524+
current_index++;
525+
}
526+
// Next will be the type of the array(s)
527+
continue;
528+
}
529+
530+
case ELEMENT_TYPE_MVAR: {
531+
// We are likely parsing a standalone generic param
532+
token_length = CorSigUncompressToken(
533+
PCCOR_SIGNATURE(&function_info.signature.data[current_index]),
534+
&type_token);
535+
current_type_name.append("T"_W);
536+
current_index += token_length;
537+
// TODO: implement conventions for generics (eg., TC1, TC2, TM1, TM2)
538+
// current_type_name.append(std::to_wstring(type_token));
539+
break;
540+
}
541+
542+
case ELEMENT_TYPE_VAR: {
543+
// We are likely within a generic variant
544+
token_length = CorSigUncompressToken(
545+
PCCOR_SIGNATURE(&function_info.signature.data[current_index]),
546+
&type_token);
547+
current_type_name.append("T"_W);
548+
current_index += token_length;
549+
// TODO: implement conventions for generics (eg., TC1, TC2, TM1, TM2)
550+
// current_type_name.append(std::to_wstring(type_token));
551+
break;
552+
}
553+
554+
case ELEMENT_TYPE_GENERICINST: {
555+
// skip past generic type indicator token
556+
current_index++;
557+
// skip past actual generic type token (probably a class)
558+
current_index++;
559+
const auto generic_type_data = RetrieveTypeForSignature(
560+
metadata_import, function_info, current_index, token_length);
561+
auto type_name = generic_type_data.name;
562+
current_type_name.append(type_name);
563+
current_type_name.append("<"_W); // Begin generic args
564+
565+
// Because we are starting a new generic, decrement any existing level
566+
if (!generic_arg_stack.empty()) {
567+
generic_arg_stack.top()--;
568+
}
569+
570+
// figure out how many generic args this type has
571+
const auto index_of_tick = type_name.find_last_of('`');
572+
auto num_args_text = ToString(type_name.substr(index_of_tick + 1));
573+
auto actual_arg_count = std::stoi(num_args_text, nullptr);
574+
generic_arg_stack.push(actual_arg_count);
575+
current_index += token_length;
576+
// Next will be the variants
577+
continue;
578+
}
579+
580+
case ELEMENT_TYPE_BYREF: {
581+
// TODO: This hasn't been encountered yet
582+
current_type_name.append("ref"_W);
583+
break;
584+
}
585+
586+
case ELEMENT_TYPE_END: {
587+
// we already handle the generic by counting args
588+
continue;
589+
}
590+
591+
default: {
592+
// This is unexpected and we should report that, and not instrument
593+
current_type_name.append(ToWSTRING(ToString(cor_element_type)));
594+
break;
595+
}
596+
}
597+
598+
if (!append_to_type.empty()) {
599+
current_type_name.append(append_to_type);
600+
append_to_type = ""_W;
601+
}
602+
603+
if (!generic_arg_stack.empty()) {
604+
// decrement this level's args
605+
generic_arg_stack.top()--;
606+
607+
if (generic_arg_stack.top() > 0) {
608+
// we're in the middle of generic type args
609+
current_type_name.append(", "_W);
610+
}
611+
}
612+
613+
while (!generic_arg_stack.empty() && generic_arg_stack.top() == 0) {
614+
// unwind the generics with no args left
615+
generic_arg_stack.pop();
616+
current_type_name.append(">"_W);
617+
618+
if (!generic_arg_stack.empty() && generic_arg_stack.top() > 0) {
619+
// We are in a nested generic and we need a comma to separate args
620+
current_type_name.append(", "_W);
621+
}
622+
}
623+
624+
if (!generic_arg_stack.empty()) {
625+
continue;
626+
}
627+
628+
if (current_type_index >= expected_number_of_types) {
629+
// We missed something, drop out for safety
630+
return false;
631+
}
632+
633+
type_names[current_type_index] = current_type_name;
634+
current_type_name = ""_W;
635+
current_type_index++;
636+
}
637+
638+
signature_result = type_names;
639+
640+
return true;
641+
}
389642
} // namespace trace

src/Datadog.Trace.ClrProfiler.Native/clr_helpers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "com_ptr.h"
1010
#include "integration.h"
11+
#include <set>
1112

1213
namespace trace {
1314
class ModuleMetadata;
@@ -307,6 +308,9 @@ mdMethodSpec DefineMethodSpec(const ComPtr<IMetaDataEmit2>& metadata_emit,
307308

308309
bool DisableOptimizations();
309310

311+
bool SignatureFuzzyMatch(const ComPtr<IMetaDataImport2>& metadata_import,
312+
const FunctionInfo& function_info,
313+
std::vector<WSTRING>& signature_result);
310314
} // namespace trace
311315

312316
#endif // DD_CLR_PROFILER_CLR_HELPERS_H_

test/Datadog.Trace.ClrProfiler.Native.Tests/Datadog.Trace.ClrProfiler.Native.Tests.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@
5454
</PropertyGroup>
5555
<ItemGroup>
5656
<ClInclude Include="pch.h" />
57+
<ClInclude Include="test_helpers.h" />
5758
</ItemGroup>
5859
<ItemGroup>
60+
<ClCompile Include="clr_helper_type_check_test.cpp" />
5961
<ClCompile Include="integration_loader_test.cpp" />
6062
<ClCompile Include="integration_test.cpp" />
6163
<ClCompile Include="clr_helper_test.cpp" />

0 commit comments

Comments
 (0)