<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8" -->

---
title: Using profiler function hooks in .NET with Silhouette
description: A deep technical walkthrough explains how to implement .NET profiler function hooks (enter/leave/tailcall callbacks) using Silhouette, a C# library for writing...
canonical: https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Using profiler function hooks in .NET with Silhouette | daily.dev
og:description: A deep technical walkthrough explains how to implement .NET profiler function hooks (enter/leave/tailcall callbacks) using Silhouette, a C# library for writing...
og:url: https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8
og:image: https://api.daily.dev/og/posts/3tWKrFrg8.png
og:image:alt: Using profiler function hooks in .NET with Silhouette
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Using profiler function hooks in .NET with Silhouette

**[Kevin Gosse \(minidump.net\)](https://daily.dev/sources/minidump)** · 8 min read · 0 upvotes · 0 comments

## Summary

A deep technical walkthrough explains how to implement .NET profiler function hooks (enter/leave/tailcall callbacks) using Silhouette, a C# library for writing .NET profilers. Since function hooks require low-level register handling impossible in managed code, the approach uses precompiled assembly stubs from Microsoft's clr-samples repository, compiled into a static library and linked directly into the profiler binary using NativeAOT's static linking support, avoiding the need to ship a separate DLL. Full working code, including registering managed callbacks via UnmanagedCallersOnly and tracing function calls with indentation, is provided along with a GitHub repository.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://minidump.net/using-function-hooks-with-silhouette>

## Questions this post answers

### How do you implement enter/leave function hooks in a .NET profiler written with Silhouette?

Function hooks require assembly stubs because the JIT does not save CPU registers before calling SetEnterLeaveFunctionHooks3WithInfo callbacks, which cannot be expressed in managed code. The solution compiles Microsoft's clr-samples asmhelpers.asm into a static library exposing EnterStub, LeaveStub, and TailcallStub, then registers managed callbacks via a RegisterCallbacks function decorated with UnmanagedCallersOnly and called from the profiler's Initialize method.

_daily.dev surfaces deep profiler-internals writeups like this for engineers building custom .NET tracing tools._

### How can NativeAOT statically link a native C++ library instead of shipping a separate DLL?

NativeAOT supports static linking by adding DirectPInvoke and NativeLibrary entries in the csproj ItemGroup, pointing to the compiled .lib file and declaring a fake library name used for p/invoke calls. This embeds the native static library directly into the final binary at compile time, avoiding the need to distribute a separate DLL alongside the profiler.

_developers choosing between dynamic and static linking in NativeAOT can track these patterns on daily.dev._

### What is the difference between a sampling profiler and a tracing profiler?

Sampling profilers periodically snapshot callstacks with low overhead, giving a good general performance overview but missing small methods and call counts. Tracing profilers record every function entry and exit for exhaustive execution data, useful for algorithmic complexity analysis, but they add fixed per-call overhead that overestimates time in small leaf functions, making them unsuitable for raw performance measurement.

_daily.dev helps developers weighing profiler strategies stay current on tracing versus sampling trade-offs._

## Similar posts on daily.dev

- [Creating a .NET CLR profiler using C\# and NativeAOT with Silhouette](https://daily.dev/posts/creating-a-net-clr-profiler-using-c-and-nativeaot-with-silhouette-nxp8lsqgl) · .NET Escapades · 6 upvotes · 0 comments
- [Making Reflection Native AOT Safe in .NET 10: DynamicallyAccessedMembers Guide](https://daily.dev/posts/making-reflection-native-aot-safe-in-net-10-dynamicallyaccessedmembers-guide-xpy12bjep) · We Are .NET · 3 upvotes · 0 comments
- [Enabling the Application Insights Profiler](https://daily.dev/posts/enabling-the-application-insights-profiler-iov3s2yke) · The Art of Simplicity · 0 upvotes · 0 comments

---

Tags: [#.net](https://daily.dev/tags/.net), [#c#](https://daily.dev/tags/c#)

[View this post on daily.dev](https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Using profiler function hooks in .NET with Silhouette","url":"https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8"},"datePublished":"2026-10-04T13:06:29.699Z","dateModified":"2026-10-04T13:16:47.164Z","description":"A deep technical walkthrough explains how to implement .NET profiler function hooks (enter/leave/tailcall callbacks) using Silhouette, a C# library for writing...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/ee6b4490beceb8e6de5200c65636c239?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/ee6b4490beceb8e6de5200c65636c239?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Kevin Gosse (minidump.net)","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Kevin Gosse (minidump.net)","logo":"https://media.daily.dev/image/upload/s--8KHn1aFB--/f_auto,q_auto/v1791119046/logos/minidump?_a=BAMAMicg0","url":"https://daily.dev/sources/minidump"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":".net,c#","timeRequired":"PT8M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Kevin Gosse (minidump.net)","item":"https://daily.dev/sources/minidump"},{"@type":"ListItem","position":3,"name":"Using profiler function hooks in .NET with Silhouette"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/using-profiler-function-hooks-in-net-with-silhouette-3twkrfrg8#faq","mainEntity":[{"@type":"Question","name":"How do you implement enter/leave function hooks in a .NET profiler written with Silhouette?","acceptedAnswer":{"@type":"Answer","text":"Function hooks require assembly stubs because the JIT does not save CPU registers before calling SetEnterLeaveFunctionHooks3WithInfo callbacks, which cannot be expressed in managed code. The solution compiles Microsoft's clr-samples asmhelpers.asm into a static library exposing EnterStub, LeaveStub, and TailcallStub, then registers managed callbacks via a RegisterCallbacks function decorated with UnmanagedCallersOnly and called from the profiler's Initialize method. daily.dev surfaces deep profiler-internals writeups like this for engineers building custom .NET tracing tools."}},{"@type":"Question","name":"How can NativeAOT statically link a native C++ library instead of shipping a separate DLL?","acceptedAnswer":{"@type":"Answer","text":"NativeAOT supports static linking by adding DirectPInvoke and NativeLibrary entries in the csproj ItemGroup, pointing to the compiled .lib file and declaring a fake library name used for p/invoke calls. This embeds the native static library directly into the final binary at compile time, avoiding the need to distribute a separate DLL alongside the profiler. developers choosing between dynamic and static linking in NativeAOT can track these patterns on daily.dev."}},{"@type":"Question","name":"What is the difference between a sampling profiler and a tracing profiler?","acceptedAnswer":{"@type":"Answer","text":"Sampling profilers periodically snapshot callstacks with low overhead, giving a good general performance overview but missing small methods and call counts. Tracing profilers record every function entry and exit for exhaustive execution data, useful for algorithmic complexity analysis, but they add fixed per-call overhead that overestimates time in small leaf functions, making them unsuitable for raw performance measurement. daily.dev helps developers weighing profiler strategies stay current on tracing versus sampling trade-offs."}}]}
```

