Git Product home page Git Product logo

clrinstrumentationengine's Introduction

CLR Instrumentation Engine

Main Branch: Build Status

Release Branch: Build Status

Overview

The CLR Instrumentation Engine (CLRIE) is a cooperation profiler that allows running multiple profiling extensions in the same process. It was built to satisfy Application Insights, IntelliTrace, and Production Breakpoints scenarios.

The CLRIE's goal is to create a cooperative environment for different profilers to work together on various platforms. In order to further this goal, we plan to open source this repo to the community by the end June 2019.

For more information about our current and future project scope and to track our progress, please see the CLRIE Road Map.

Getting Started

The CLR Instrumentation Engine is a profiler implementation and is enabled and configured via environment variables for the running process.

Contributing

Please read Contributing for details on the Contributor License Agreement (CLA) and the process for submitting pull requests to us.

This repo builds using Visual Studio 2022 and requires the following components:

Component Id Component Friendly Name
Microsoft.Component.MSBuild MSBuild
Microsoft.VisualStudio.Workload.NativeDesktop Desktop development with C++ (Workload)
Microsoft.VisualStudio.Component.VC.ATL.Spectre C++ ATL for latest v143 build tools with Spectre Mitigations (x86 & x64)
Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre MSVC v143 - VS 2022 C++ x64/x86 Spectre-mitigated libs (Latest)

Optionally, in order to develop Wixproj files in Visual Studio, you will need to install the Wix Toolset Visual 2022 Extension, also known as the "Votive" extension.

  • Design Notes - the overall design for CLR Instrumentation Engine.
  • Build - how to run local builds.
  • Test - how to run tests.
  • Release Process - how to release CLRIE to various platforms.

Versioning

The CLR Instrumentation Engine follows the Semantic Versioning scheme.

Encountering and reporting issues

See Troubleshooting for common pitfalls and remediation.

If you're still encountering a non-critical issues, please contact [email protected].

Critical security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) at [email protected]. See Security for more details.

License

Usage governance of the Instrumentation Engine is detailed in the License.

Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct. For more information, see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft’s Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies.

clrinstrumentationengine's People

Contributors

abhitejjohn avatar cablooshe avatar clguiman avatar delmyers avatar dependabot[bot] avatar dividedmind avatar hughbe avatar jacdavis avatar jakubch1 avatar jamiemagee avatar jander-msft avatar kkeirstead avatar m0nkey653 avatar mattwarren avatar microsoftopensource avatar msftgits avatar plnelson avatar robertpi avatar schmittjoseph avatar step-security-bot avatar tzwlai avatar wiktork avatar williamxiemsft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clrinstrumentationengine's Issues

Introduce Yaml builds

This will allow external partners to update the build definitions if they want to add new changes and offloads some of the maintainability from internal teams.

For PullRequests, collabors/admins must comment "/azp run" in order to actually invoke the PR build.

Getting Generic Parameter and Return Value Types

For a method that has generic parameter and/or return value types, I am unable to get more than the CorElementType : ELEMENT_TYPE_GENERICINST (which is 0x15).

I'm doing this from the InstrumentMethod callback. The method that I am inspecting (through InstrumentMethod has a return value type which is a generic class:

System.Collections.Generic.IEnumerable<netfwWebapi.Models.Customer>

My code gets the CorSignature and parses it using the ISignatureParser packaged in the CLR Instrumentation Engine. Here is some code:

	// Get CorSignature
	BYTE pSignature[256] = {};
	DWORD cbSignature = 0;
	pMethodInfo->GetCorSignature(_countof(pSignature), pSignature, &cbSignature);

	CComPtr<IModuleInfo> pModuleInfo;
	pMethodInfo->GetModuleInfo(&pModuleInfo);

	ModuleID moduleId;
	pModuleInfo->GetModuleID(&moduleId);

	// Parse method signature
	CComPtr<ITypeCreator> pTypeFactory;
	pModuleInfo->CreateTypeFactory(&pTypeFactory);
	CComQIPtr<ISignatureParser> pSigParser(pTypeFactory);

	CComPtr<IType> pReturnType;
	CComPtr<IEnumTypes> pEnumTypes;
	DWORD genericParameterCount = 0;
	DWORD sizeUsed = 0;

	HRESULT hr = pSigParser->ParseMethodSignature(
		pSignature,
		cbSignature,
		nullptr,
		&pReturnType,
		&pEnumTypes,
		&genericParameterCount,
		&sizeUsed);
        if (FAILED(hr))
        {
             // logs error and exit out
        }

	// Return Value type
	CorElementType etReturnValue;
	pReturnType->GetCorElementType(&etReturnValue);
	m_returnValueType = getTypeString(etReturnValue, pReturnType, pMethodInfo);

My getTypeString method does something special if the element type is ELEMENT_TYPE_GENERICINST. Here is what it does for generic types:

		CComPtr<IType> pDeclaringType;
		pMethodInfo->GetDeclaringType(&pDeclaringType);
		CComPtr<ITokenType> pDeclaringTokenType;
		pDeclaringType->QueryInterface(__uuidof(ITokenType), (LPVOID*)&pDeclaringTokenType);
		mdToken declaringTypeToken = mdTokenNil;
		pDeclaringTokenType->GetToken(&declaringTypeToken);
		PLog.Write(LogLevel::Info, "Declaring Type Token: 0x%x\n", declaringTypeToken);

		mdToken itemTypeToken = mdTokenNil;
		CComPtr<ITokenType> pItemTokenType;
		pType->QueryInterface(__uuidof(ITokenType), (LPVOID*)&pItemTokenType);

		if (pItemTokenType != nullptr)
		{
			pItemTokenType->GetToken(&itemTypeToken);
			PLog.Write(LogLevel::Info, "ItemTypeToken: 0x%x\n", itemTypeToken);
		}
		else
		{
			PLog.Write(LogLevel::Info, "ItemTypeToken is null\n");
		}

As you can see, I ultimately want to get a type token for the return value type passed to my getTypeString method. What I get is that pItemTokenType is a null pointer. Is there something I am missing that's available to an InstrumentationMethod to get the generic type information? Do I need to get an IMetaDataImport interface pointer (yes, I know I can get one easily) to retrieve this info, or is there a simpler way?

Thanks!

Creating a Profiler Proxy

Azure App Service preinstalled site extension allows for automatic updates to the latest version using the tilda '~' semantic. This behavior is desirable as it allows for automatic security bugfixes and maintainability.

For the Windows distribution model using MSI, having similar behavior helps different profiler products install different versions of the CLRIE while guaranteeing the latest versions will be used. This also decouples updates to CLRIE from any specific profiler as users can install the latest engine themselves.

Add in-depth diagnostics logging

This type of logging should be granular to the InstrumentationMethod level, allowing easier triaging of conflicts. Consider introducing a new logging flag for this.

Attach: Create attach data schema

Create a JSON schema that allows for passing engine and instrumentation method configuration information to the CLR Instrumentation Engine during profiler attach.

IMethodInfo::GetFullName does not return the full name of async state machine methods

I found this while debugging my instrumentation method. I was trying to match the name of an async state machine method, and it seems that ::GetFullName() is just returning the typename of the inner type. It is not encapsulating it in the outer type and namespace. The way that ILDASM does this is:

<namespace>.<TypeName>+<InnerType>.MethodName

Initial Use Questions

Background

I've worked on .NET profilers before, and in fact have one that works (with the ICorProfilerCallback interfaces). This is my first stab at adding my profiler as an InstrumentationMethod.

Configuration setup

I'm trying to use the engine in an IIS-based web app, so I've setup the environment variables in the
I've setup the following environment variables in HKLM\SYSTEM\CurrentControlSet\Services\W3WC and ...\Services\WAS. This is just how I would do it with a raw profiler hook. Environment looks like this:

COR_PROFILER={324F817A-7420-4E6D-B3C1-143FBED6D855}
COR_ENABLE_PROFILING=1
COR_PROFILER_PATH_64=c:\path-to\MicrosoftInstrumentationEngine_x64.dll
MicrosoftInstrumentationEngine_Host={CA487940-57D2-10BF-11B2-A3AD5A13CBC0}
MicrosoftInstrumentationEngine_HostPath_64=C:\path-to\Microsoft.InstrumentationEngine.ExtensionsHost_x64.dll
MicrosoftInstrumentationEngine_LogLevel=Errors
MicrosoftInstrumentationEngine_ConfigPath64_ShiftLeft=C:\path-to\MyInstrumentation.config

MyInstrumentation.config:

<InstrumentationEngineConfiguration>
  <InstrumentationMethod>
    <Name>MyInstrumentation</Name>
    <Description>My Instrumentation for Monitoring</Description>
    <Module>Profiler.dll</Module>
    <ClassGuid>{8539E781-D72A-42FE-85A6-E27D6162F5B5}</ClassGuid>
    <Priority>50</Priority>
  </InstrumentationMethod>
</InstrumentationEngineConfiguration>

InstrumentationMethod

I've got a class that implements IInstrumentationMethod.Initialize and IInstrumentationMethod.ShouldInstrumentMethod.

Behavior

When I make requests against the web application (it's a simple .NET Framework-based WebApi), I noticed that MicrosoftInstrumentationEngine_x64.dll is loaded into the w3wp.exe process, but not my Profiler.dll. I'm not sure why it's not picking up my InstrumentationMethod from the config file.

Attach: Create instrumentation method attach interfaces

Create new instrumentation method interfaces that allow instrumentation methods to participate in attaching the CLR Instrumentation Engine to a .NET CLR instance. These interfaces should allow for the initialization of the instrumentation method, passing configuration information during attach initialization, and notifying of attach completion.

Engine Only Profiles First CLR Instance

The current Instrumentation Engine supports initializing for the first CLR instance in a process. All subsequent CLR instances that are loaded into the process are not profiled by the Instrumentation Engine. While the vast majority of managed processes will only ever have a single CLR instance, there are scenarios where there are multiple CLRs within a process.

For example, an IIS worker process that has managed IIS modules and an in-process ASP.NET Core 2.2+ application will have two CLRs: the .NET Framework CLR and the .NET Core 2.2+ CLR. In this example, the Instrumentation Engine typically attaches to the .NET Framework CLR instance and rejects profiling of the .NET Core CLR instance. Typically, it is more interesting to profile the .NET Core CLR instance rather than or in addition to the .NET Framework CLR instance.

In order to support multiple CLR instances in the same process, the Instrumentation Engine will be changed as follows:

  • The Engine will create a new instance of CProfilerManager for each CLR instance. Each of these instances are completely separate so that there is no intermingling of CLR information across the instances.
  • Each available Instrumentation Method (IM) will have a new instance of its IInstrumentationMethod implementation created and initialized per CLR instance. Thus, each IM instance will only be associated a single IProfilerManager instance (which is associated with a single CLR instance). It will be left to the IM instance to determine if it wants to participate in profiling the associated CLR instance.

Eliminate global locks on JIT compilation

We are getting reports that Instrumentation Engine shows up on hot path in Application Insights Service Profiler. Like below:

image

@juliusl investigated an issue and believe this is caused by global lock Instrumentation Engine sets on JIT Compilation and other callbacks.

clr.dll being loaded unexpectedly by InstrumentationEngine.Extensions.Base

Code that loads clr.dll (in extension base component of ApplicationInsights)

if (bstrModuleName == L"mscorlib.dll")
    {
        std::wstring strModulePath;
        IfFailRet(GetModulePath(strModulePath));

        std::shared_ptr<CModuleMetaDataLoader> spLoader;
        IfFailRet(CreateAndBuildUp(spLoader, strModulePath + ExtensionsBaseOriginalModuleName));
HRESULT CModuleMetaDataLoader::Load()
{
    HRESULT hr = S_OK;

    ATL::CComPtr<IMetaDataDispenserEx> pDisp;

    ATL::CComPtr<ICLRMetaHost> pMetaHost;
    ATL::CComPtr<ICLRRuntimeInfo> pRuntime;
    IfFailRet(CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, reinterpret_cast<void **>(&pMetaHost)));
    IfFailRet(pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, reinterpret_cast<void **>(&pRuntime)));
    IfFailRet(pRuntime->GetInterface(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenserEx, reinterpret_cast<void **>(&pDisp)));

The highlighted line causes the following dll’s to load, mentioned earlier in the thread:

clr.dll     clr.dll     C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll            N/A        N/A        Symbols loaded.                C:\Users\wiktork\AppData\Local\Temp\SymbolCache\clr.pdb\8354a7bf1cd24de5827c9128420bda702\clr.pdb                25           4.7.3190.0 built by: NET472REL1LAST_C 8/6/2018 11:16 PM         742B0000-7499F000        [1616] ConsoleApplication9.exe                              
msvcr120_clr0400.dll      msvcr120_clr0400.dll      C:\Windows\SysWOW64\msvcr120_clr0400.dll  N/A        N/A                Symbols loaded. C:\Users\wiktork\AppData\Local\Temp\SymbolCache\msvcr120_clr0400.i386.pdb\915fa55e7772460c947f83560680967a1\msvcr120_clr0400.i386.pdb                27           12.00.52519.0 built by: VSWINSERVICING             12/8/2016 4:21 PM         741B0000-742A5000                [1616] ConsoleApplication9.exe                

CLRIE Message logging is too verbose

Currently, the LogLevel Message causes extremely high overhead (to the point of uselessness for anything except development time, and even then it's quite large).

In addition, the current logging infrastructure could be refactored/restructured to delay dynamic allocations of strings for prefixes such as time from the InstrumentationMethod to some public interface supporting logging with VarArgs by the ProfilerManager. This would reduce memory size taken by CLRIE (and any snapshots generated) by reducing the memory page changes due to logging. This latter discussion could be moved to another Issue, but I want to jot it down somewhere for now.

Support bitness for Raw Profiler Hook environment variables

Current implementation only supports one variable which doesn't make sense as we aren't guaranteed the bitness of the process. Solution would be to separate into two variables (_32 & _64) and have the non-bitness variable as a fallback.

InstrumentationEngine.ProfilerProxy_x(86|64).dll activation gets stuck on malformed version strings

Consider the following folder structure which contains the malformed version string 1.99

snikolov@DESKTOP-5UO7643:/mnt/c/Program Files/Microsoft CLR Instrumentation Engine$ find
. -type f

./1.99/Instrumentation32/MicrosoftInstrumentationEngine_x86.dll
./1.99/Instrumentation64/MicrosoftInstrumentationEngine_x64.dll
./Proxy/v1/InstrumentationEngine.ProfilerProxy_x64.dll
./Proxy/v1/InstrumentationEngine.ProfilerProxy_x86.dll

Observed behavior - on activation the profiler proxy becomes permanently stuck.
Expected behavior - the profiler proxy shall exit with an error code and print a message either on stdout/stderr or the file pointed to by MicrosoftInstrumentationEngine_FileLogPath.

Rename folder 1.99 to something more sensible like 1.0.28 and observe that now the profiler proxy works as designed.

Midl-generated annotations for optional out parameters are incorrect.

Out parameters for basic non-interface and non-VARIANT types (e.g., [out, optional] ULONG*) are incorrect. Midl does not generate SAL for optional basic types of out paramters. I believe the correct annotation is [in, out, ptr] for these types, but I will have to double check.

Generalize Linux ExtensionHost

Current implementation of the Linux ExtensionsHost (which deals with loading Instrumentation Methods) specifically loads Snapshot Debugger's method "Production Breakpoints". This needs to be generalized to have functional parity with its Windows counterpart.

Can't debug instrumentation method initialization when using retail binaries.

ClrInstrumentationEngine allows you to debug your instrumentation method by setting an environment variable to wait for a debugger attach. This is not practical for customers, though, because the ProfilerManager.cpp sets up a wait time that is only overridden for debugging when the dll is compiled for debugging.

Though clients can attempt to locate the correct release tag, and rebuild with debug if they are on Windows, I expect most clients would rather just use retail dlls, and debug their own code.

I suggest that we remove the #ifdef Debug for the override of the wait time.

Generate MSI/MSM from wixproj

In order to support a cooperative model for our partners, we want to provide an MSI/MSM installer which defines the common installation behavior for everyone to use on Windows Desktop scenarios.

Migrate CLRIE to VS 2019

CLRIE currently depends on VS 2017 toolsets and builds. VS 2019 was released on April 2nd, 2019 and we are internally using it for daily development.

Unless there's strong pushback to continue developing using VS 2017, we will migrate this project to build with the latest tools provided by VS 2019.

Turning on/off Application Insights in Azure App Service portal resets InstrumentationEngine_EXTENSION_VERSION to disabled

Environment

Azure App Service

Scenario

I have a .NET profiler installed in the App Service, and have InstrumentationEngine_EXTENSION_VERSION set to ~1 in the Application settings, via the portal:

I enabled Application Insights via the portal (Deployment -> Application Insights). When I go back to look at the Application settings, InstrumentationEngine_EXTENSION_VERSION is reset to disabled.

I noticed that it gets reset to disabled for any Application Insights feature I enable, and even when I disabled Application Insights, it reset it!

This is clearly a bug or at least not acceptable behavior because, with CLRIE, it's possible to have multiple profilers running. My workaround has been to manually check the InstrumentationEngine_EXTENSION_VERSION application setting and set it when necessary.

Reproducing this bug should be easy given the explanation above.

Although this is not necessarily a CLR Instrumentation Engine bug, I was advised that reporting it here was one way to get some movement on fixing it.

Consider NuGet package deployment for Instrumentation Engine

Providing Instrumentation Engine as a NuGet package would allow profilers to deploy with the app.

This model however would be difficult to cooperate on (if two profilers require different CLRIE versions, the user must resolve this). Consider providing guidance/documentation on how to use CLRIE NuGet in a cooperative manner.

Support .NET < 4.0 and COM registration for CLRInstrumentationEngine on Windows

Followed the build instructions and successfully generated all build artifacts.
Also ensured that all tests pass, namely 37 of 37, 42/42, 59/59 all pass.

In section Environment Variables, in the first occurrence of COR_PROFILER I see a bracketed UUID which implies that successful DLL registration is necessary, especially for framework versions < 4.

However, when I attempt DLL registration as shown below from an admin prompt, it fails with error 0x80070715, i.e. "The specified resource type cannot be found in the image file."

C:\Windows\SysWOW64\regsvr32.exe /i C:\tmp\CLRInstrumentationEngine\bin\Debug\x64\InstrumentationEngine\x64\MicrosoftInstrumentationEngine_x64.dll

The above DLL is missing typical COM sections like REGISTRY, TYPELIB, String Table.
See the attached image.
missing_com_sections

Build tool version: vs2019 Professional 16.3.5
Host OS: Windows 10, 1903, build 18362.418

What am I missing here?
See also a forward link to my previous message.

Parameterize ConfigLoading waittimeout

Suggested from #111,
Currently we wait 1 minute for configs to load which is sufficient for a small number of configs. When more configs exist, this timeout may not be sufficient and we should expose configuration hooks to allow customers to get unblocked.

CLRIE Roadmap link is 404

The link to the CLRIE Roadmap in the Overview section of the repository's README.md goes to a 404. Knowing the roadmap for this project would be helpful for vendors and users of the instrumentation engine.

Expose InstrumentationEngine.Headers NuGet package

In order for users and partners to write InstrumentationMethods, they would need to include InstrumentationEngine.h header file. This file is available in-repo, but to ease consumption of CLRIE it should be made available as a NuGet package in a public feed.

CLR Will No Longer Release Profiler COM Object

A change to the .NET Core CLR has been made such that the deconstructor of attached profilers will no longer be called: https://github.com/dotnet/coreclr/issues/15136#issuecomment-534325458

The advice given is that all shutdown logic must be contained within ICorProfilerCallback::Shutdown since the CLR will never release the COM object. The current Instrumentation Engine does some minimal work in CProfilerManager::~CProfilerManager, which needs to be done elsewhere (e.g. ICorProfilerCallback::Shutdown).

Cooperative Distribution Model Requirements

Overview

The CLR Instrumentation Engine's main purpose is to provide cooperation among profilers running in the same environment. This requires various users to cooperatively ensure that the latest version of the Engine can be detected and loaded into the process.

To achieve this goal, we are asking each partner team to participate and provide their needs and requirements for their scenarios. Once we have enough information, we can discuss distribution solutions that can hopefully meet everyone's needs.

Distribution Steps

The distribution story for the Engine can be described through a series of steps.

  1. Detect if the Instrumentation Engine is installed.
  2. Detect if the Instrumentation Engine is configured.
  3. Install the Instrumentation Engine.
  4. Configure/activate the Instrumentation Engine.

Requirements

These are the currently identified requirements we expect for the Engine distribution. Please let us know if you have feedback or conflicts with these requirements and reply with your responses to these questions about your product.

  • Support single well-known installation folder to easily detect if installed.
  • Support updating the Engine via side-by-side versions to avoid file locks on older versions.
  • Support determining latest version so profiler clients can detect if it meets their minimum requirements. The Engine is required to be forward compatible
  • Setting Environment Variables in a consistent and well-known manner.
  • Support ASP.NET Desktop apps on Windows and ASP.NET Core apps on both Windows and Linux.
  • Don't require clean-up of older versions. The feasibility of this is dependent on the installation technologies and techniques.
  • Have a validation mechanism against the installation

Questions

  • What are the installation technologies you are currently using (eg. MSI, Exe bundles, xcopy, scripts)? Please differentiate between different platforms.
  • What are the minimum requirements on your target platform?
    • Windows versions & skus
    • Linux distros & versions
    • Framework requirements (.Net Framework/Core versions)
    • Supported application types (ASP.NET on IIS, WPF, etc.)
  • Is installation expected or allowed to run with admin privileges (ie. be able to install into %programfiles%)? Do you require installing to a non-admin accessible folder?

Possible Solutions

There may be multiple solutions, but the two options we have considered internally are:

  1. MSI and/or a MergedModule for Windows platform

Each partner team has mentioned using an MSI for Windows to install their product. MSI has the benefit of supporting the above requirements.

If we go this route, there are several implementation details to consider:

  • Share at the component level or chain in our MSI
  • Custom actions for detection
  • Per-user versus per-machine installation
  • 1 MSI per version
  1. Installation Scripts

There are several options for scripting languages, including Python, Bash, and PowerShell which allow for cross platform support and customizable control over installation. This also allows for use of various packaging frameworks such as NuGet or distro-specific options like apk, deb files. Several concerns about this option:

  • Ability for MSIs or existing distribution technologies to incorporate this into their installation.
  • Introducing new requirements (eg. dotnet core 2.1 for PS Core)

Fold ExtensionsHost into InstrumentationEngine

This PR removes the notion of a customizable ExtensionsHost in favor of the default host (which has been used for years). This reduces the attack surface of the Engine, reduces contention points for collaboration over different hosts, and simplifies the onboarding process for the Engine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.