# Exception Handling Write Through Optimization.
-Write through is an optimization done on local variables that live across exception handling flow like a handler, filter, or finally so that they can be enregistered - treated as a register candidate - throughout a method. For each variable live across one of these constructs, the minimum requirement is that a store to the variables location on the stack is placed between a reaching definition and any point of control flow leading to the handler, as well as a load between any return from a filter or finally and an upward exposed use. Conceptually this maintains the value of the variable on the stack across the exceptional flow which would kill any live registers. This transformation splits a local variable into multiple enregisterable compiler temporaries backed by the local variable on the stack. For local vars that additionally have appearances within a eh construct, a load from the stack local is inserted to a temp that will be enregistered within the handler.
+Write through is an optimization done on local variables that live across
+exception handling flow like a handler, filter, or finally so that they can be
+enregistered - treated as a register candidate - throughout a method. For each
+variable live across one of these constructs, the minimum requirement is that a
+store to the variables location on the stack is placed between a reaching
+definition and any point of control flow leading to the handler, as well as a
+load between any return from a filter or finally and an upward exposed use.
+Conceptually this maintains the value of the variable on the stack across the
+exceptional flow which would kill any live registers. This transformation splits
+a local variable into an enregisterable compiler temporary backed by
+the local variable on the stack. For local vars that additionally have
+appearances within an eh construct, a load from the stack local is inserted to
+a temp that will be enregistered within the handler.
## Motivation
-Historically the JIT has not done this transformation because exception handling was rare and thus the transformation was not worth the compile time. Additionally it was easy to make the recomendation to users to remove EH from performance critical methods since they had control of where the EH appeared. Neither of these points remain true as we increase our focus on cloud workloads. The use of non-blocking async calls are common in performance critical paths for these workloads and async injects exception handling constructs to implement the feature. This in combination with the long standing use of EH in 'foreach' and 'using' statements means that we are seeing EH constructs that are difficult for the user to manage or remove high in the profile (Techempower on Kestrel is a good example). Given these cloud workloads doing the transformation would be a clear benefit.
+Historically the JIT has not done this transformation because exception
+handling was rare and thus the transformation was not worth the compile time.
+Additionally it was easy to make the recomendation to users to remove EH from
+performance critical methods since they had control of where the EH appeared.
+Neither of these points remain true as we increase our focus on cloud
+workloads. The use of non-blocking async calls are common in performance
+critical paths for these workloads and async injects exception handling
+constructs to implement the feature. This in combination with the long
+standing use of EH in 'foreach' and 'using' statements means that we are seeing
+EH constructs that are difficult for the user to manage or remove high in the
+profile (Techempower on Kestrel is a good example). It's also good to consider
+that in MSIL, basic operations can raise semantically meaningful exceptions
+(unlike say C++, where an explicit throw is required to raise an exception) so
+injected handlers can end up pessimizing a number of local variables in the
+method. Given this combination of issues in cloud workloads doing the
+transformation should be a clear benefit.
## Design
-The goal of the design is to preserve the constraints listed above - i.e. preserve a correct value on the stack for any local var that crosses an EH edge in the flow graph. To ensure that the broad set of global optimizations can act on the IR shape produced by this transformation and that phase ordering issues do not block enregistration opportunities the write through phase will be staged just prior to SSA build after morph and it will do a full walk of the IR rewriting appearances to proxies as well as inserting reloads at the appropriate blocks in the flow graph as indicated by EH control flow semantics. To preserve the needed values on the stack a store will also be inserted after every definition to copy the new value in the proxy back to the stack location. This will leave non optimal number of stores (too many) but with the strategy that the more expensive analysis to eliminate/better place stores will be staged as a global optimization in a higher compilation tier.
+The goal of the design is to preserve the constraints listed above - i.e.
+preserve a correct value on the stack for any local var that crosses an EH edge
+in the flow graph. To ensure that the broad set of global optimizations can act
+on the IR shape produced by this transformation and that phase ordering issues
+do not block enregistration opportunities the write through phase will be
+staged just prior to SSA build after morph and it will do a full walk of the
+IR rewriting appearances to proxies as well as inserting reloads at the
+appropriate blocks in the flow graph as indicated by EH control flow semantics.
+To preserve the needed values on the stack a store will also be inserted after
+every definition to copy the new value in the proxy back to the stack location.
+This will leave non optimal number of stores (too many) but with the strategy
+that the more expensive analysis to eliminate/better place stores will be
+staged as a global optimization in a higher compilation tier.
+
+There are a number of wrinkles informing this design based on how the JIT models EH:
+- The jit does not explicitly model the exception flow, so a given block and
+ even a given statement within a block may have multiple exception-raising sites.
+- For statements within protected regions, and for all variables live into any
+ reachable handler, the jit assumes all definitions within the region can
+ potentially reach uses in the handlers, since the exact interleaving of
+ definition points and exception points is not known. Hence every definition
+ is a reaching definition, even both values back from to back stores with no
+ read of the variable in between.
+- The jit does not model which handlers are reachable from a given protected region,
+ so considers a variable live into a handler if it is live into any handler in the method.
+
+It is posible to do better than the "store every definition" approch outlined
+in the design, but the expectation is that this would require posibly
+modifying the model in the JIT and staging more throughput intensive analyses.
+With these considerations this design was selected and further improvements
+left to future optimization.
### Throughput
-To identify EH crossing local vars global liveness is necessary. This comes at the significant cost of the liveness analysis. To mitigate this the write through phase is staged immediately before SSA build for the global optimizer. Since the typical case is that there is no EH, the liveness analysis in write through can be reused directly by SSA build. For the case where EH local vars are present liveness today must be rebuilt for SSA since new local vars have been added, but incremental update to the RyuJIT liveness analysis can be implemented (worklist based live analysis) to improve the throughput. Additionally the write through transformation does a full IR walk - also expensive - to replace EH local var appearances with proxies and insert transfers to and from the stack for EH flow, given this initial implementations may need to be staged as part of AOT (crossgen) compiles until tiering can move the more expensive analysis out of the startup path.
+To identify EH crossing local vars global liveness is necessary. This comes at
+the significant cost of the liveness analysis. To mitigate this the write
+through phase is staged immediately before SSA build for the global optimizer.
+Since the typical case is that there is no EH, the liveness analysis in write
+through can be reused directly by SSA build. For the case where EH local vars
+are present liveness today must be rebuilt for SSA since new local vars have
+been added, but incremental update to the RyuJIT liveness analysis can be
+implemented (worklist based live analysis) to improve the throughput.
+Additionally the write through transformation does a full IR walk - also
+expensive - to replace EH local var appearances with proxies and insert
+transfers to and from the stack for EH flow, given this initial implementations
+may need to be staged as part of AOT (crossgen) compiles until tiering can move
+the more expensive analysis out of the startup path.
### Algorithm
+
On the IR directly before SSA build:
-- Run global liveness to identify local vars that cross EH boundaries (as a byproduct of this these local vars are marked "do not enregister")
+- Run global liveness to identify local vars that cross EH boundaries (as a
+ byproduct of this these local vars are marked "do not enregister")
- Foreach EH local var create a new local var "proxy" that can be enregisterd.
- Iterate each block in the flow graph doing the following:
* Foreach tree in block do a post order traversal and
- Replace all appearances of EH local vars with the defined proxy
- Insert a copy of proxy definition back to the EH local var (on the stack)
- * If EH handler entry block insert reloads from EH local var to proxy at block head
- * If finally or filter exit, insert reloads from EH local var to proxy at successor block heads
-- For method entry block, insert reloads from parameter EH local vars to proxies
+ * If EH handler entry block insert reloads from EH local var to proxy at
+ block head
+ * If finally or filter exit, insert reloads from EH local var to proxy at
+ successor block heads
+- For method entry block, insert reloads from parameter EH local vars to
+ proxies
-At end no proxy should be live across EH flow and all value updates will be written back to the stack location.
+At end no proxy should be live across EH flow and all value updates will be
+written back to the stack location.
## Next steps
-The initial prototype that produced the example bellow is currently being improved to make it production ready. At the same time a more extensive suite of example tests are being developed.
+The initial prototype that produced the example bellow is currently being
+improved to make it production ready. At the same time a more extensive suite
+of example tests are being developed.
- [X] Proof of concept prototype.
- [ ] Production implementation of WriteThru phase.
## Example
-The following is a simple example that shows enregistration for a local var live, and modified, through a catch.
+The following is a simple example that shows enregistration for a local var
+live, and modified, through a catch.
#### Source code snippet
The following issues illustrate some of the motivation for improving the handling of value types
(structs) in RyuJIT:
-* VSO Bug 98404: .NET JIT x86 - poor code generated for value type initialization
+* [\#11407 [RyuJIT] Fully enregister structs that fit into a single register when profitable](https://github.com/dotnet/coreclr/issues/11407), also VSO Bug 98404: .NET JIT x86 - poor code generated for value type initialization
* This is a simple test case that should generate simply `xor eax; ret` on x86 and x64, but
instead generates many unnecessary copies. It is addressed by full enregistration of
structs that fit into a register:
- **Windows_NT x64 Release Priority 1 Build & Test:** "test Windows_NT pri1"
- **Windows_NT x64 Release IL RoundTrip Build & Test:** "test Windows_NT ilrt"
-- **Windows_NT x64 Release Long-Running GC Build & Test:**: "test Windows_NT x64 Release longgc"
+- **Windows_NT x64 Release Long-Running GC Build & Test:**: "test Windows_NT Release longgc"
+- **Windows_NT x64 Release GC Simulator:**: "test Windows_NT Release gcsimulator"
+- **Windows_NT x64 Release Standalone GC:**: "test Windows_NT Release standalone_gc"
+- **Windows_NT x64 Release GC Reliability Framework:**: "test Windows_NT Release gc_reliability_framework"
- **Windows_NT x64 Release Ready-To-Run Priority 0 Build & Test:** "test Windows_NT Release r2r"
- **Windows_NT x64 Checked Ready-To-Run Priority 0 Build & Test:** "test Windows_NT Checked r2r"
- **Windows_NT x64 Release Ready-To-Run Priority 1 Build & Test:** "test Windows_NT Release pri1r2r"
- **Windows_NT x64 Checked GCStress=0xc JitStress=2 Build & Test:** "test Windows_NT gcstress0xc_jitstress2"
- **Windows_NT x64 Checked GCStress=0xc MinOpts Heap Verify 1 Build & Test:** "test Windows_NT gcstress0xc_minopts_heapverify1"
- **Windows_NT x64 Checked Long-Running GC Build & Test:**: "test Windows_NT x64 Checked longgc"
+- **Windows_NT x64 Checked Standalone GC:**: "test Windows_NT Checked standalone_gc"
+- **Windows_NT x64 Checked GC Reliability Framework:**: "test Windows_NT Checked gc_reliability_framework"
- **Windows_NT x64 Formatting:**: "test Windows_NT formatting"
- **Windows_NT x64 Checked CoreFX Baseline Build & Test:** "test Windows_NT corefx_baseline"
- **Windows_NT x64 Checked CoreFX MinOpts Build & Test:** "test Windows_NT corefx_minopts"
- **Ubuntu x64 Release Priority 1 Build & Test:** "test Ubuntu pri1"
- **Ubuntu x64 Release IL RoundTrip Build & Test:** "test Ubuntu ilrt"
- **Ubuntu x64 Release Long-Running GC Build & Test:**: "test Ubuntu Release longgc"
+- **Ubuntu x64 Release GC Simulator:**: "test Ubuntu Release gcsimulator"
+- **Ubuntu x64 Release Standalone GC:**: "test Ubuntu Release standalone_gc"
+- **Ubuntu x64 Checked Standalone GC:**: "test Ubuntu Checked standalone_gc"
+- **Ubuntu x64 Release GC Reliability Framework:**: "test Ubuntu Release gc_reliability_framework"
+- **Ubuntu x64 Checked GC Reliability Framework:**: "test Ubuntu Checked gc_reliability_framework"
- **Ubuntu x64 Release Ready-To-Run Priority 0 Build & Test:** "test Ubuntu Release r2r"
- **Ubuntu x64 Checked Ready-To-Run Priority 0 Build & Test:** "test Ubuntu Checked r2r"
- **Ubuntu x64 Release Ready-To-Run Priority 1 Build & Test:** "test Ubuntu Release pri1r2r"
- **OSX x64 Release Priority 1 Build & Test:** "test OSX pri1"
- **OSX x64 Release IL RoundTrip Build & Test:** "test OSX ilrt"
- **OSX x64 Release Long-Running GC Build & Test:**: "test OSX Release longgc"
+- **OSX x64 Release GC Simulator:**: "test OSX10.12 Release gcsimulator"
+- **OSX x64 Release Standalone GC:**: "test OSX10.12 Release standalone_gc"
+- **OSX x64 Checked Standalone GC:**: "test OSX10.12 Checked standalone_gc"
+- **OSX x64 Release GC Reliability Framework:**: "test OSX10.12 Release gc_reliability_framework"
+- **OSX x64 Checked GC Reliability Framework:**: "test OSX10.12 Checked gc_reliability_framework"
- **OSX x64 Release Ready-To-Run Priority 0 Build & Test:** "test OSX Release r2r"
- **OSX x64 Checked Ready-To-Run Priority 0 Build & Test:** "test OSX Checked r2r"
- **OSX x64 Release Ready-To-Run Priority 1 Build & Test:** "test OSX Release pri1r2r"
We use and recommend the following workflow:
1. Create an issue for your work.
- - You can skip this step for trivial changes.
- - Reuse an existing issue on the topic, if there is one.
- - Use [CODE_OWNERS.TXT](https://github.com/dotnet/coreclr/blob/master/CODE_OWNERS.TXT) to find relevant maintainers and @ mention them to ask for feedback on your issue.
- - Get agreement from the team and the community that your proposed change is a good one.
- - If your change adds a new API, follow the [API Review Process](https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/api-review-process.md).
- - Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
+ - You can skip this step for trivial changes.
+ - Reuse an existing issue on the topic, if there is one.
+ - Use [CODE_OWNERS.TXT](https://github.com/dotnet/coreclr/blob/master/CODE_OWNERS.TXT) to find relevant maintainers and @ mention them to ask for feedback on your issue.
+ - Get agreement from the team and the community that your proposed change is a good one.
+ - If your change adds a new API, follow the [API Review Process](https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/api-review-process.md).
+ - Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
2. Create a personal fork of the repository on GitHub (if you don't already have one).
3. Create a branch off of master (`git checkout -b mybranch`).
- - Name the branch so that it clearly communicates your intentions, such as issue-123 or githubhandle-issue.
- - Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
+ - Name the branch so that it clearly communicates your intentions, such as issue-123 or githubhandle-issue.
+ - Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
4. Make and commit your changes.
- - Please follow our [Commit Messages](https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/contributing-workflow.md#commit-messages) guidance.
+ - Please follow our [Commit Messages](https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/contributing-workflow.md#commit-messages) guidance.
5. Add new tests corresponding to your change, if applicable.
6. Build the repository with your changes.
- - Make sure that the builds are clean.
- - Make sure that the tests are all passing, including your new tests.
+ - Make sure that the builds are clean.
+ - Make sure that the tests are all passing, including your new tests.
7. Create a pull request (PR) against the upstream repository's **master** branch.
- - Push your changes to your fork on GitHub (if you haven't already).
+ - Push your changes to your fork on GitHub (if you haven't already).
Note: It is OK for your PR to include a large number of commits. Once your change is accepted, you will be asked to squash your commits into one or some appropriately small number of commits before your PR is merged.
The C++/CLI language was standardized as [ECMA 372](http://www.ecma-international.org/publications/standards/Ecma-372.htm) in 2005.
-EMCA 372 is supported by the .NET Framework, but not .NET Core.
+ECMA 372 is supported by the .NET Framework, but not .NET Core.
**ECMA 372 Resources**
## Stress Testing ##
Stress testing must run for at least **48 hours** against a debug build.
-Instructions for running stress are located in the repo at tests\src\GC\Stress\stress_run_readme.txt.
+Stress testing for checked and release builds can be done on pull requests with The .NET CI infrastructure.
+A stress run can be requested using the trigger phrase:
+
+```
+@dotnet_bot test <platform> <flavor> gc_reliability_framework
+```
+
+This will run the stress framework for the default amount of time (15 hours) on the given platform and build flavor.
## Functional Testing ##
A functional test run executes the same code as a stress run, but only runs for 30 minutes.
Instructions for running stress are located in the repo at tests\src\GC\Stress\stress_run_readme.txt.
+It is recommended that you run at least some of the below PR-triggered CI jobs:
+
+```
+@dotnet_bot test Windows_NT Checked longgc
+@dotnet_bot test OSX10.12 Checked longgc
+@dotnet_bot test Ubuntu Checked longgc
+@dotnet_bot test Windows_NT Checked standalone_gc
+@dotnet_bot test OSX10.12 Checked standalone_gc
+@dotnet_bot test Ubuntu Checked standalone_gc
+```
+
+The "Long GC" tests are a series of GC tests whose running time is too long or memory usage is too high to run with
+the rest of the Priority 0 unit tests. The "Standalone GC" build mode builds and runs the GC in a semi-standalone manner
+(see https://github.com/dotnet/coreclr/projects/3).
+
+You may also wish to run the GC Simulator tests. They may take up to 24 hours to complete and are known to sometimes fail on Ubuntu
+due to poor interactions with the Linux OOM killer. However, they have proven to be quite useful in finding bugs in the past:
+
+```
+@dotnet_bot test Windows_NT Release gcsimulator
+@dotnet_bot test Ubuntu Release gcsimulator
+@dotnet_bot test OSX10.12 Release gcsimulator
+```
+
## Performance Testing ##
Coming soon.
REM don't precompile anything from CoreCLR
if /I exist %CORE_ROOT_STAGE%\%2 exit /b 0
-"%CORE_ROOT_STAGE%\crossgen.exe" /Platform_Assemblies_Paths "%CORE_ROOT%" "%1" >nul 2>nul
+"%CORE_ROOT_STAGE%\crossgen.exe" /Platform_Assemblies_Paths "%CORE_ROOT%" /in "%1" /out "%CORE_ROOT%/temp.ni.dll" >nul 2>nul
set /a __exitCode = %errorlevel%
if "%__exitCode%" == "-2146230517" (
echo %2 is not a managed assembly.
echo Unable to precompile %2
exit /b 0
)
+
+:: Delete original .dll & replace it with the Crossgened .dll
+del %1
+ren "%CORE_ROOT%\temp.ni.dll" %2
echo Successfully precompiled %2
exit /b 0
# Event Logging Infrastructure
__GeneratedIntermediate="$__IntermediatesDir/Generated"
__GeneratedIntermediateEventProvider="$__GeneratedIntermediate/eventprovider_new"
+ __GeneratedIntermediateEventPipe="$__GeneratedIntermediate/eventpipe_new"
+
if [[ -d "$__GeneratedIntermediateEventProvider" ]]; then
rm -rf "$__GeneratedIntermediateEventProvider"
fi
+ if [[ -d "$__GeneratedIntermediateEventPipe" ]]; then
+ rm -rf "$__GeneratedIntermediateEventPipe"
+ fi
+
if [[ ! -d "$__GeneratedIntermediate/eventprovider" ]]; then
mkdir -p "$__GeneratedIntermediate/eventprovider"
fi
+ if [[ ! -d "$__GeneratedIntermediate/eventpipe" ]]; then
+ mkdir -p "$__GeneratedIntermediate/eventpipe"
+ fi
+
mkdir -p "$__GeneratedIntermediateEventProvider"
+ mkdir -p "$__GeneratedIntermediateEventPipe"
+
if [[ $__SkipCoreCLR == 0 || $__ConfigureOnly == 1 ]]; then
echo "Laying out dynamically generated files consumed by the build system "
echo "Laying out dynamically generated Event Logging Test files"
exit
fi
+ case $__BuildOS in
+ Linux)
+ echo "Laying out dynamically generated EventPipe Implementation"
+ $PYTHON -B -Wall -Werror "$__ProjectRoot/src/scripts/genEventPipe.py" --man "$__ProjectRoot/src/vm/ClrEtwAll.man" --intermediate "$__GeneratedIntermediateEventPipe" --exc "$__ProjectRoot/src/vm/ClrEtwAllMeta.lst"
+ if [[ $? != 0 ]]; then
+ exit
+ fi
+ ;;
+ *)
+ ;;
+ esac
+
#determine the logging system
case $__BuildOS in
Linux)
fi
rm -rf "$__GeneratedIntermediateEventProvider"
+
+ echo "Cleaning the temp folder of dynamically generated EventPipe files"
+ $PYTHON -B -Wall -Werror -c "import sys;sys.path.insert(0,\"$__ProjectRoot/src/scripts\"); from Utilities import *;UpdateDirectory(\"$__GeneratedIntermediate/eventpipe\",\"$__GeneratedIntermediateEventPipe\")"
+ if [[ $? != 0 ]]; then
+ exit
+ fi
+
+ rm -rf "$__GeneratedIntermediateEventPipe"
}
build_native()
<DefineConstants Condition="'$(FeatureMacl)' == 'true'">$(DefineConstants);FEATURE_MACL</DefineConstants>
<DefineConstants Condition="'$(FeatureManagedEtw)' == 'true'">$(DefineConstants);FEATURE_MANAGED_ETW</DefineConstants>
<DefineConstants Condition="'$(FeatureManagedEtwChannels)' == 'true'">$(DefineConstants);FEATURE_MANAGED_ETW_CHANNELS</DefineConstants>
- <DefineConstants Condition="'$(FeatureNongenericCollections)' == 'true'">$(DefineConstants);FEATURE_NONGENERIC_COLLECTIONS</DefineConstants>
<DefineConstants Condition="'$(FeaturePal)' == 'true'">$(DefineConstants);FEATURE_PAL</DefineConstants>
<DefineConstants Condition="'$(FeaturePathCompat)' == 'true'">$(DefineConstants);FEATURE_PATHCOMPAT</DefineConstants>
<DefineConstants Condition="'$(FeatureXplatEventSource)' == 'true'">$(DefineConstants);FEATURE_EVENTSOURCE_XPLAT</DefineConstants>
if(FEATURE_EVENT_TRACE)
add_definitions(-DFEATURE_EVENT_TRACE=1)
endif(FEATURE_EVENT_TRACE)
-add_definitions(-DFEATURE_PERFTRACING)
+if(CLR_CMAKE_PLATFORM_LINUX)
+ add_definitions(-DFEATURE_PERFTRACING)
+endif(CLR_CMAKE_PLATFORM_LINUX)
if(CLR_CMAKE_PLATFORM_UNIX)
add_definitions(-DFEATURE_EVENTSOURCE_XPLAT=1)
endif(CLR_CMAKE_PLATFORM_UNIX)
add_definitions(-DFEATURE_UNIX_AMD64_STRUCT_PASSING)
endif (CLR_CMAKE_PLATFORM_UNIX_AMD64)
add_definitions(-DFEATURE_USE_ASM_GC_WRITE_BARRIERS)
-if(CLR_CMAKE_PLATFORM_ARCH_AMD64 AND NOT WIN32)
+if((CLR_CMAKE_PLATFORM_ARCH_AMD64 OR CLR_CMAKE_PLATFORM_ARCH_ARM64) AND NOT WIN32)
add_definitions(-DFEATURE_MANUALLY_MANAGED_CARD_BUNDLES)
add_definitions(-DFEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP)
-endif(CLR_CMAKE_PLATFORM_ARCH_AMD64 AND NOT WIN32)
+endif((CLR_CMAKE_PLATFORM_ARCH_AMD64 OR CLR_CMAKE_PLATFORM_ARCH_ARM64) AND NOT WIN32)
if(WIN32)
add_definitions(-DFEATURE_VERSIONING_LOG)
endif(WIN32)
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Source of truth for dependency tooling: the commit hash of the dotnet/versions master branch as of the last auto-upgrade. -->
<PropertyGroup>
- <CoreFxCurrentRef>4a2c8b78ad4065f39c0894ccba79902822989b2c</CoreFxCurrentRef>
- <CoreClrCurrentRef>4a2c8b78ad4065f39c0894ccba79902822989b2c</CoreClrCurrentRef>
+ <CoreFxCurrentRef>b78ca796620b226c33af1583ca49e889c83c722c</CoreFxCurrentRef>
+ <CoreClrCurrentRef>b78ca796620b226c33af1583ca49e889c83c722c</CoreClrCurrentRef>
</PropertyGroup>
<!-- Auto-upgraded properties for each build info dependency. -->
<PropertyGroup>
- <CoreFxExpectedPrerelease>preview2-25303-04</CoreFxExpectedPrerelease>
+ <CoreFxExpectedPrerelease>preview2-25309-01</CoreFxExpectedPrerelease>
</PropertyGroup>
<!-- Full package version strings that are used in other parts of the build. -->
<PropertyGroup>
- <CoreClrPackageVersion>2.0.0-preview2-25303-03</CoreClrPackageVersion>
+ <CoreClrPackageVersion>2.0.0-preview2-25309-01</CoreClrPackageVersion>
<XunitPackageVersion>2.2.0-beta2-build3300</XunitPackageVersion>
</PropertyGroup>
'gcsimulator',
'jitdiff',
'standalone_gc',
+ 'gc_reliability_framework',
'illink'] + r2rJitStressScenarios
def static configurationList = ['Debug', 'Checked', 'Release']
return (scenario == 'jitdiff')
}
+def static isGcReliabilityFramework(def scenario) {
+ return (scenario == 'gc_reliability_framework')
+}
+
+def static scenarioNeedsPri1Build(def scenario) {
+ return (scenario == 'pri1' || scenario == 'pri1r2r' || scenario == 'gcstress15_pri1r2r'|| scenario == 'coverage' || isGcReliabilityFramework(scenario))
+}
+
def static setTestJobTimeOut(newJob, scenario) {
if (isGCStressRelatedTesting(scenario)) {
Utilities.setJobTimeout(newJob, 4320)
else if (isJitDiff(scenario)) {
Utilities.setJobTimeout(newJob, 240)
}
+ else if (isGcReliabilityFramework(scenario)) {
+ Utilities.setJobTimeout(newJob, 1440)
+ }
// Non-test jobs use the default timeout value.
}
// addEmailPublisher(job, 'dotnetgctests@microsoft.com')
Utilities.addPeriodicTrigger(job, '@weekly')
break
+ case 'gc_reliability_framework':
+ assert (os == 'Ubuntu' || os == 'Windows_NT' || os == 'OSX10.12')
+ assert (configuration == 'Release' || configuration == 'Checked')
+ // Only triggered by phrase.
+ break
case 'ilrt':
assert !(os in bidailyCrossList)
// ILASM/ILDASM roundtrip one gets a daily build, and only for release
Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Simulator", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
}
break
+ case 'standalone_gc':
+ if (configuration == 'Release' || configuration == 'Checked') {
+ Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Standalone GC", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
+ }
+ break
+ case 'gc_reliability_framework':
+ if (configuration == 'Release' || configuration == 'Checked') {
+ Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Reliability Framework", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
+ }
+ break
case 'minopts':
case 'forcerelocs':
case 'jitstress1':
Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} Standalone GC", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
}
break
+ case 'gc_reliability_framework':
+ if (configuration == 'Release' || configuration == 'Checked') {
+ Utilities.addGithubPRTriggerForBranch(job, branch, "${os} ${architecture} ${configuration} GC Reliability Framework", "(?i).*test\\W+${os}\\W+${configuration}\\W+${scenario}.*")
+ }
+ break
case 'minopts':
case 'forcerelocs':
case 'jitstress1':
// binaries are sent to a default directory whose name is about
// 35 characters long.
- else if (scenario == 'pri1' || scenario == 'pri1r2r' || scenario == 'gcstress15_pri1r2r'|| scenario == 'coverage') {
+ else if (scenarioNeedsPri1Build(scenario)) {
buildCommands += "set __TestIntermediateDir=int&&build.cmd ${lowerConfiguration} ${arch} ${buildOpts} -priority=1"
}
else if (isLongGc(scenario)) {
buildCommands += "%WORKSPACE%\\tests\\runtest.cmd ${runtestArguments} TestEnv ${stepScriptLocation}"
}
}
+ else if (isGcReliabilityFramework(scenario)) {
+ buildCommands += "tests\\runtest.cmd ${runtestArguments} GenerateLayoutOnly"
+ buildCommands += "tests\\scripts\\run-gc-reliability-framework.cmd ${arch} ${configuration}"
+ Utilities.addArchival(newJob, "stdout.txt")
+ Utilities.addArchival(newJob, "Logs/**")
+ }
else if (architecture == 'x64' || architecture == 'x86') {
buildCommands += "tests\\runtest.cmd ${runtestArguments}"
}
Utilities.setJobTimeout(newJob, 240)
}
- buildCommands += "set __TestIntermediateDir=int&&build.cmd skiptests ${lowerConfiguration} ${architecture} toolset_dir C:\\ats2"
+ buildCommands += "set __TestIntermediateDir=int&&build.cmd ${lowerConfiguration} ${architecture} toolset_dir C:\\ats2 -priority=1"
// Test build and run are launched together.
buildCommands += "python tests\\scripts\\arm64_post_build.py -repo_root %WORKSPACE% -arch ${architecture} -build_type ${lowerConfiguration} -scenario ${scenario} -key_location C:\\tools\\key.txt"
//Utilities.addXUnitDotNETResults(newJob, 'bin/tests/testResults.xml')
return
}
break
+ case 'gc_reliability_framework':
case 'standalone_gc':
if (os != 'Windows_NT' && os != 'Ubuntu' && os != 'OSX10.12') {
return
return
}
break
+ case 'gc_reliability_framework':
case 'standalone_gc':
if (configuration != 'Release' && configuration != 'Checked') {
return
}
+ break
case 'coverage':
//We only want Ubuntu Release for coverage
if (os != 'Ubuntu') {
// so we didn't create a build only job for windows_nt specific to that stress mode. Just copy
// from the default scenario
def testBuildScenario = scenario
- if (testBuildScenario == 'coverage' || testBuildScenario == 'pri1r2r'|| testBuildScenario == 'gcstress15_pri1r2r') {
+ if (scenarioNeedsPri1Build(scenario)) {
testBuildScenario = 'pri1'
}
else if ( testBuildScenario == 'r2r' || Constants.r2rJitStressScenarios.indexOf(testBuildScenario) != -1 || isLongGc(testBuildScenario)) {
def runilasmroundtripStr = ''
def gcstressStr = ''
def illinkStr = ''
+ def layoutOnlyStr =''
if (scenario == 'r2r' ||
scenario == 'pri1r2r' ||
}
}
+ if (isGcReliabilityFramework(scenario)) {
+ layoutOnlyStr = '--build-overlay-only'
+ }
+
def folder = getJobFolder(scenario)
def newJob = job(Utilities.getFullJobName(project, jobName, isPR, folder)) {
// Add parameters for the inputs
--mscorlibDir=\"\${WORKSPACE}/bin/Product/${osGroup}.${architecture}.${configuration}\" \\
--coreFxBinDir=\"\${WORKSPACE}/bin/CoreFxBinDir\" \\
--limitedDumpGeneration \\
- ${testEnvOpt} ${serverGCString} ${gcstressStr} ${crossgenStr} ${runcrossgentestsStr} ${runjitstressStr} ${runjitstressregsStr} ${runjitmioptsStr} ${runjitforcerelocsStr} ${runjitdisasmStr} ${runilasmroundtripStr} ${illinkStr} ${sequentialString} ${playlistString}""")
+ ${testEnvOpt} ${serverGCString} ${gcstressStr} ${crossgenStr} ${runcrossgentestsStr} ${runjitstressStr} \\
+ ${runjitstressregsStr} ${runjitmioptsStr} ${runjitforcerelocsStr} ${runjitdisasmStr} ${runilasmroundtripStr} \\
+ ${illinkStr} ${sequentialString} ${playlistString} ${layoutOnlyStr}""")
+
+ if (isGcReliabilityFramework(scenario)) {
+ // runtest.sh doesn't actually execute the reliability framework - do it here.
+ if (serverGCString != '') {
+ shell("export COMPlus_gcServer=1")
+ }
+
+ shell("./tests/scripts/run-gc-reliability-framework.sh ${architecture} ${configuration}")
+ }
}
}
}
+ if (isGcReliabilityFramework(scenario))
+ {
+ // Both of these are emitted by the RF
+ Utilities.addArchival(newJob, "stdout.txt")
+ Utilities.addArchival(newJob, "Logs/**")
+ }
+
if (scenario == 'coverage') {
// Publish coverage reports
Utilities.addHtmlPublisher(newJob, '${WORKSPACE}/coverage/Coverage/reports', 'Code Coverage Report', 'coreclr.html')
{
parameters
{
- stringParam('XUNIT_PERFORMANCE_MAX_ITERATION', '2', 'Sets the number of iterations to one. We want to do this so that we can run as fast as possible as this is just for smoke testing')
- stringParam('XUNIT_PERFORMANCE_MAX_ITERATION_INNER_SPECIFIED', '2', 'Sets the number of iterations to one. We want to do this so that we can run as fast as possible as this is just for smoke testing')
+ stringParam('XUNIT_PERFORMANCE_MAX_ITERATION', '2', 'Sets the number of iterations to two. We want to do this so that we can run as fast as possible as this is just for smoke testing')
+ stringParam('XUNIT_PERFORMANCE_MAX_ITERATION_INNER_SPECIFIED', '2', 'Sets the number of iterations to two. We want to do this so that we can run as fast as possible as this is just for smoke testing')
+ }
+ }
+ else
+ {
+ parameters
+ {
+ stringParam('XUNIT_PERFORMANCE_MAX_ITERATION', '21', 'Sets the number of iterations to twenty one. We are doing this to limit the amount of data that we upload as 20 iterations is enought to get a good sample')
+ stringParam('XUNIT_PERFORMANCE_MAX_ITERATION_INNER_SPECIFIED', '21', 'Sets the number of iterations to twenty one. We are doing this to limit the amount of data that we upload as 20 iterations is enought to get a good sample')
}
}
def configuration = 'Release'
batchFile("tests\\runtest.cmd ${configuration} ${architecture} GenerateLayoutOnly")
+ // Run with just stopwatch
batchFile("tests\\scripts\\run-xunit-perf.cmd -arch ${arch} -configuration ${configuration} -testBinLoc bin\\tests\\${os}.${architecture}.${configuration}\\performance\\perflab\\Perflab -library -generateBenchviewData \"%WORKSPACE%\\Microsoft.Benchview.JSONFormat\\tools\" ${uploadString} -runtype ${runType}")
batchFile("tests\\scripts\\run-xunit-perf.cmd -arch ${arch} -configuration ${configuration} -testBinLoc bin\\tests\\${os}.${architecture}.${configuration}\\Jit\\Performance\\CodeQuality -generateBenchviewData \"%WORKSPACE%\\Microsoft.Benchview.JSONFormat\\tools\" ${uploadString} -runtype ${runType}")
+
+ // Run with the full set of counters enabled
+ batchFile("tests\\scripts\\run-xunit-perf.cmd -arch ${arch} -configuration ${configuration} -testBinLoc bin\\tests\\${os}.${architecture}.${configuration}\\performance\\perflab\\Perflab -library -generateBenchviewData \"%WORKSPACE%\\Microsoft.Benchview.JSONFormat\\tools\" ${uploadString} -runtype ${runType} -collectionFlags default+BranchMispredictions+CacheMisses+InstructionRetired+gcapi")
+ batchFile("tests\\scripts\\run-xunit-perf.cmd -arch ${arch} -configuration ${configuration} -testBinLoc bin\\tests\\${os}.${architecture}.${configuration}\\Jit\\Performance\\CodeQuality -generateBenchviewData \"%WORKSPACE%\\Microsoft.Benchview.JSONFormat\\tools\" ${uploadString} -runtype ${runType} -collectionFlags default+BranchMispredictions+CacheMisses+InstructionRetired+gcapi")
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
- <_PlatformDoesNotSupportNiFiles Condition="'$(Platform)' == 'arm'">true</_PlatformDoesNotSupportNiFiles>
- <_PlatformDoesNotSupportNiFiles Condition="'$(Platform)' == 'armel'">true</_PlatformDoesNotSupportNiFiles>
- <_PlatformDoesNotSupportNiFiles Condition="'$(Platform)' == 'x86'">true</_PlatformDoesNotSupportNiFiles>
+ <_PlatformDoesNotSupportCreatedump Condition="'$(Platform)' == 'arm'">true</_PlatformDoesNotSupportCreatedump>
+ <_PlatformDoesNotSupportCreatedump Condition="'$(Platform)' == 'armel'">true</_PlatformDoesNotSupportCreatedump>
+ <_PlatformDoesNotSupportCreatedump Condition="'$(Platform)' == 'x86'">true</_PlatformDoesNotSupportCreatedump>
+ <_PlatformDoesNotSupportCreatedump Condition="'$(_runtimeOSFamily)' == 'tizen'">true</_PlatformDoesNotSupportCreatedump>
<_PlatformDoesNotSupportEventTrace Condition="'$(_runtimeOSFamily)' == 'tizen'">true</_PlatformDoesNotSupportEventTrace>
<_PlatformDoesNotSupportEventTrace Condition="'$(Platform)' == 'x86'">true</_PlatformDoesNotSupportEventTrace>
</PropertyGroup>
<NativeBinary Include="$(BinDir)System.Globalization.Native.so" />
<NativeBinary Include="$(BinDir)sosdocsunix.txt" />
<NativeBinary Include="$(BinDir)System.Private.CoreLib.dll" />
+ <NativeBinary Condition="'$(_PlatformDoesNotSupportCreatedump)' != 'true'" Include="$(BinDir)createdump" />
<ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
</ItemGroup>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<NoStdLib>true</NoStdLib>
<NoCompilerStandardLib>true</NoCompilerStandardLib>
+ <IsDotNetFrameworkProductAssembly>true</IsDotNetFrameworkProductAssembly>
<UseOpenKey Condition="'$(UseOpenKey)'==''">true</UseOpenKey>
<!-- We don't use any of MSBuild's resolution logic for resolving the framework, so just set these two properties to any folder that exists to skip
the GenerateReferenceAssemblyPaths task (not target) and to prevent it from outputting a warning (MSB3644). -->
<_TargetFrameworkDirectories>$(MSBuildThisFileDirectory)/Documentation</_TargetFrameworkDirectories>
<_FullFrameworkReferenceAssemblyPaths>$(MSBuildThisFileDirectory)/Documentation</_FullFrameworkReferenceAssemblyPaths>
-
</PropertyGroup>
<!-- Default configurations to help VS understand the options -->
return false;
}
}
- TRACE("Thread %04x RIP %016llx RSP %016llx\n", m_tid, m_gpRegisters.rip, m_gpRegisters.rsp);
+ TRACE("Thread %04x RIP %016llx RSP %016llx\n", m_tid, (unsigned long long)m_gpRegisters.rip, (unsigned long long)m_gpRegisters.rsp);
return true;
}
PAL__wcstoui64
PAL_wcstoul
PAL_iswprint
+PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange
PAL_wcslen
PAL_wcsncmp
PAL_wcsrchr
)
endif(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_EVENT_TRACE)
+if(CLR_CMAKE_PLATFORM_LINUX)
+ list(APPEND CORECLR_LIBRARIES
+ eventpipe
+ )
+endif(CLR_CMAKE_PLATFORM_LINUX)
+
target_link_libraries(coreclr ${CORECLR_LIBRARIES})
if(WIN32)
ULONG fn__ThreadpoolMgr__AsyncTimerCallbackCompletion;
ULONG fn__DACNotifyCompilationFinished;
ULONG fn__ThePreStub;
+
+#ifdef _TARGET_ARM_
+ ULONG fn__ThePreStubCompactARM;
+#endif // _TARGET_ARM_
+
ULONG fn__ThePreStubPatchLabel;
ULONG fn__PrecodeFixupThunk;
ULONG fn__StubDispatchFixupStub;
typedef ArrayDPTR(const BYTE) PTR_CBYTE;
typedef DPTR(INT8) PTR_INT8;
typedef DPTR(INT16) PTR_INT16;
+typedef DPTR(UINT16) PTR_UINT16;
typedef DPTR(WORD) PTR_WORD;
typedef DPTR(USHORT) PTR_USHORT;
typedef DPTR(DWORD) PTR_DWORD;
GenTreePtr op1 = treeNode->gtGetOp1();
var_types targetType = treeNode->TypeGet();
+ // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
+ // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
+ // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
+ assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
+
#ifdef DEBUG
if (targetType == TYP_VOID)
{
}
//------------------------------------------------------------------------
-// genCodeForTreeNode Generate code for a single node in the tree.
-//
-// Preconditions:
-// All operands have been evaluated.
-//
-void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
-{
- regNumber targetReg = treeNode->gtRegNum;
- var_types targetType = treeNode->TypeGet();
- emitter* emit = getEmitter();
-
-#ifdef DEBUG
- lastConsumedNode = nullptr;
- if (compiler->verbose)
- {
- unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
- compiler->gtDispLIRNode(treeNode, "Generating: ");
- }
-#endif
-
- // contained nodes are part of their parents for codegen purposes
- // ex : immediates, most LEAs
- if (treeNode->isContained())
- {
- return;
- }
-
- switch (treeNode->gtOper)
- {
- case GT_LCLHEAP:
- genLclHeap(treeNode);
- break;
-
- case GT_CNS_INT:
- case GT_CNS_DBL:
- genSetRegToConst(targetReg, targetType, treeNode);
- genProduceReg(treeNode);
- break;
-
- case GT_NOT:
- case GT_NEG:
- genCodeForNegNot(treeNode);
- break;
-
- case GT_OR:
- case GT_XOR:
- case GT_AND:
- assert(varTypeIsIntegralOrI(treeNode));
- __fallthrough;
-
- case GT_ADD_LO:
- case GT_ADD_HI:
- case GT_SUB_LO:
- case GT_SUB_HI:
- case GT_ADD:
- case GT_SUB:
- case GT_MUL:
- genConsumeOperands(treeNode->AsOp());
- genCodeForBinary(treeNode);
- break;
-
- case GT_LSH:
- case GT_RSH:
- case GT_RSZ:
- case GT_ROR:
- genCodeForShift(treeNode);
- break;
-
- case GT_LSH_HI:
- case GT_RSH_LO:
- genCodeForShiftLong(treeNode);
- break;
-
- case GT_CAST:
- // Cast is never contained (?)
- noway_assert(targetReg != REG_NA);
-
- if (varTypeIsFloating(targetType) && varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double <--> double/float
- genFloatToFloatCast(treeNode);
- }
- else if (varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double --> int32/int64
- genFloatToIntCast(treeNode);
- }
- else if (varTypeIsFloating(targetType))
- {
- // Casts int32/uint32/int64/uint64 --> float/double
- genIntToFloatCast(treeNode);
- }
- else
- {
- // Casts int <--> int
- genIntToIntCast(treeNode);
- }
- // The per-case functions call genProduceReg()
- break;
-
- case GT_LCL_FLD_ADDR:
- case GT_LCL_VAR_ADDR:
- {
- // Address of a local var. This by itself should never be allocated a register.
- // If it is worth storing the address in a register then it should be cse'ed into
- // a temp and that would be allocated a register.
- noway_assert(targetType == TYP_BYREF);
- noway_assert(!treeNode->InReg());
-
- inst_RV_TT(INS_lea, targetReg, treeNode, 0, EA_BYREF);
- }
- genProduceReg(treeNode);
- break;
-
- case GT_LCL_FLD:
- genCodeForLclFld(treeNode->AsLclFld());
- break;
-
- case GT_LCL_VAR:
- genCodeForLclVar(treeNode->AsLclVar());
- break;
-
- case GT_STORE_LCL_FLD:
- genCodeForStoreLclFld(treeNode->AsLclFld());
- break;
-
- case GT_STORE_LCL_VAR:
- genCodeForStoreLclVar(treeNode->AsLclVar());
- break;
-
- case GT_RETFILT:
- // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in
- // the return register, if it's not already there. The processing is the same as GT_RETURN.
- if (targetType != TYP_VOID)
- {
- // For filters, the IL spec says the result is type int32. Further, the only specified legal values
- // are 0 or 1, with the use of other values "undefined".
- assert(targetType == TYP_INT);
- }
-
- __fallthrough;
-
- case GT_RETURN:
- genReturn(treeNode);
- break;
-
- case GT_LEA:
- // if we are here, it is the case where there is an LEA that cannot
- // be folded into a parent instruction
- genLeaInstruction(treeNode->AsAddrMode());
- break;
-
- case GT_IND:
- genConsumeAddress(treeNode->AsIndir()->Addr());
- emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(treeNode), targetReg, treeNode->AsIndir());
- genProduceReg(treeNode);
- break;
-
- case GT_MOD:
- case GT_UDIV:
- case GT_UMOD:
- // We shouldn't be seeing GT_MOD on float/double args as it should get morphed into a
- // helper call by front-end. Similarly we shouldn't be seeing GT_UDIV and GT_UMOD
- // on float/double args.
- noway_assert(!varTypeIsFloating(treeNode));
- __fallthrough;
-
- case GT_DIV:
- {
- genConsumeOperands(treeNode->AsOp());
-
- noway_assert(targetReg != REG_NA);
-
- GenTreePtr dst = treeNode;
- GenTreePtr src1 = treeNode->gtGetOp1();
- GenTreePtr src2 = treeNode->gtGetOp2();
- instruction ins = genGetInsForOper(treeNode->OperGet(), targetType);
- emitAttr attr = emitTypeSize(treeNode);
- regNumber result = REG_NA;
-
- // dst can only be a reg
- assert(!dst->isContained());
-
- // src can be only reg
- assert(!src1->isContained() || !src2->isContained());
-
- if (varTypeIsFloating(targetType))
- {
- // Floating point divide never raises an exception
-
- emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
- }
- else // an signed integer divide operation
- {
- // TODO-ARM-Bug: handle zero division exception.
-
- emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
- }
-
- genProduceReg(treeNode);
- }
- break;
-
- case GT_INTRINSIC:
- genIntrinsic(treeNode);
- break;
-
- case GT_EQ:
- case GT_NE:
- case GT_LT:
- case GT_LE:
- case GT_GE:
- case GT_GT:
- genCodeForCompare(treeNode->AsOp());
- break;
-
- case GT_JTRUE:
- genCodeForJumpTrue(treeNode);
- break;
-
- case GT_JCC:
- {
- GenTreeJumpCC* jcc = treeNode->AsJumpCC();
-
- assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
-
- CompareKind compareKind = ((jcc->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
- emitJumpKind jumpKind = genJumpKindForOper(jcc->gtCondition, compareKind);
-
- inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
- }
- break;
-
- case GT_RETURNTRAP:
- genCodeForReturnTrap(treeNode->AsOp());
- break;
-
- case GT_STOREIND:
- genCodeForStoreInd(treeNode->AsStoreInd());
- break;
-
- case GT_COPY:
- // This is handled at the time we call genConsumeReg() on the GT_COPY
- break;
-
- case GT_LIST:
- case GT_FIELD_LIST:
- case GT_ARGPLACE:
- // Nothing to do
- break;
-
- case GT_PUTARG_STK:
- genPutArgStk(treeNode->AsPutArgStk());
- break;
-
- case GT_PUTARG_REG:
- genPutArgReg(treeNode->AsOp());
- break;
-
- case GT_CALL:
- genCallInstruction(treeNode->AsCall());
- break;
-
- case GT_LOCKADD:
- case GT_XCHG:
- case GT_XADD:
- genLockedInstructions(treeNode->AsOp());
- break;
-
- case GT_MEMORYBARRIER:
- instGen_MemoryBarrier();
- break;
-
- case GT_CMPXCHG:
- NYI("GT_CMPXCHG");
- genProduceReg(treeNode);
- break;
-
- case GT_RELOAD:
- // do nothing - reload is just a marker.
- // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
- // into the register specified in this node.
- break;
-
- case GT_NOP:
- break;
-
- case GT_NO_OP:
- if (treeNode->gtFlags & GTF_NO_OP_NO)
- {
- noway_assert(!"GTF_NO_OP_NO should not be set");
- }
- else
- {
- instGen(INS_nop);
- }
- break;
-
- case GT_ARR_BOUNDS_CHECK:
- genRangeCheck(treeNode);
- break;
-
- case GT_PHYSREG:
- if (treeNode->gtRegNum != treeNode->AsPhysReg()->gtSrcReg)
- {
- inst_RV_RV(INS_mov, treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg, targetType);
-
- genTransferRegGCState(treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg);
- }
- break;
-
- case GT_PHYSREGDST:
- break;
-
- case GT_NULLCHECK:
- {
- assert(!treeNode->gtOp.gtOp1->isContained());
- regNumber addrReg = genConsumeReg(treeNode->gtOp.gtOp1);
- emit->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
- }
- break;
-
- case GT_CATCH_ARG:
-
- noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
-
- /* Catch arguments get passed in a register. genCodeForBBlist()
- would have marked it as holding a GC object, but not used. */
-
- noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
- genConsumeReg(treeNode);
- break;
-
- case GT_PINVOKE_PROLOG:
- noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
-
- // the runtime side requires the codegen here to be consistent
- emit->emitDisableRandomNops();
- break;
-
- case GT_LABEL:
- genPendingCallLabel = genCreateTempLabel();
- treeNode->gtLabel.gtLabBB = genPendingCallLabel;
- emit->emitIns_J_R(INS_adr, EA_PTRSIZE, genPendingCallLabel, treeNode->gtRegNum);
- break;
-
- case GT_CLS_VAR_ADDR:
- emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
- genProduceReg(treeNode);
- break;
-
- case GT_STORE_DYN_BLK:
- case GT_STORE_BLK:
- genCodeForStoreBlk(treeNode->AsBlk());
- break;
-
- case GT_JMPTABLE:
- genJumpTable(treeNode);
- break;
-
- case GT_SWITCH_TABLE:
- genTableBasedSwitch(treeNode);
- break;
-
- case GT_ARR_INDEX:
- genCodeForArrIndex(treeNode->AsArrIndex());
- break;
-
- case GT_ARR_OFFSET:
- genCodeForArrOffset(treeNode->AsArrOffs());
- break;
-
- case GT_IL_OFFSET:
- // Do nothing; these nodes are simply markers for debug info.
- break;
-
- default:
- {
-#ifdef DEBUG
- char message[256];
- _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
- GenTree::NodeName(treeNode->OperGet()));
- NYIRAW(message);
-#else
- NYI("unimplemented node");
-#endif
- }
- break;
- }
-}
-
-//------------------------------------------------------------------------
// genLockedInstructions: Generate code for the locked operations.
//
// Notes:
NYI_ARM("genCodeForInitBlkUnroll");
}
-void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
-{
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitDisableGC();
- }
- bool isCopyBlk = blkOp->OperIsCopyBlkOp();
-
- switch (blkOp->gtBlkOpKind)
- {
- case GenTreeBlk::BlkOpKindHelper:
- if (isCopyBlk)
- {
- genCodeForCpBlk(blkOp);
- }
- else
- {
- genCodeForInitBlk(blkOp);
- }
- break;
- case GenTreeBlk::BlkOpKindUnroll:
- if (isCopyBlk)
- {
- genCodeForCpBlkUnroll(blkOp);
- }
- else
- {
- genCodeForInitBlkUnroll(blkOp);
- }
- break;
- default:
- unreached();
- }
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitEnableGC();
- }
-}
-
//------------------------------------------------------------------------
// genCodeForNegNot: Produce code for a GT_NEG/GT_NOT node.
//
}
//------------------------------------------------------------------------
+// genCodeForDivMod: Produce code for a GT_DIV/GT_UDIV/GT_MOD/GT_UMOD node.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForDivMod(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_DIV, GT_UDIV, GT_MOD, GT_UMOD));
+
+ // We shouldn't be seeing GT_MOD on float/double args as it should get morphed into a
+ // helper call by front-end. Similarly we shouldn't be seeing GT_UDIV and GT_UMOD
+ // on float/double args.
+ noway_assert(tree->OperIs(GT_DIV) || !varTypeIsFloating(tree));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+ emitter* emit = getEmitter();
+
+ genConsumeOperands(tree);
+
+ noway_assert(targetReg != REG_NA);
+
+ GenTreePtr dst = tree;
+ GenTreePtr src1 = tree->gtGetOp1();
+ GenTreePtr src2 = tree->gtGetOp2();
+ instruction ins = genGetInsForOper(tree->OperGet(), targetType);
+ emitAttr attr = emitTypeSize(tree);
+ regNumber result = REG_NA;
+
+ // dst can only be a reg
+ assert(!dst->isContained());
+
+ // src can be only reg
+ assert(!src1->isContained() || !src2->isContained());
+
+ if (varTypeIsFloating(targetType))
+ {
+ // Floating point divide never raises an exception
+
+ emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
+ }
+ else // an signed integer divide operation
+ {
+ // TODO-ARM-Bug: handle zero division exception.
+
+ emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
+ }
+
+ genProduceReg(tree);
+}
+
+//------------------------------------------------------------------------
// genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT node.
//
// Arguments:
}
//------------------------------------------------------------------------
+// genCodeForJcc: Produce code for a GT_JCC node.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForJcc(GenTreeJumpCC* tree)
+{
+ assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
+
+ CompareKind compareKind = ((tree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
+ emitJumpKind jumpKind = genJumpKindForOper(tree->gtCondition, compareKind);
+
+ inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
+}
+
+//------------------------------------------------------------------------
// genCodeForReturnTrap: Produce code for a GT_RETURNTRAP node.
//
// Arguments:
{
inst_RV_RV(INS_mov, targetReg, REG_RDX, targetType);
}
+
+ genProduceReg(treeNode);
#else // !0
NYI("genCodeForMulHi");
#endif // !0
}
-// generate code for a DIV or MOD operation
-//
-void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
-{
- // unused on ARM64
-}
-
// Generate code for ADD, SUB, MUL, DIV, UDIV, AND, OR and XOR
// This method is expected to have called genConsumeOperands() before calling it.
void CodeGen::genCodeForBinary(GenTree* treeNode)
GenTreePtr op1 = treeNode->gtGetOp1();
var_types targetType = treeNode->TypeGet();
+ // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
+ // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
+ // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
+ assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
+
#ifdef DEBUG
if (targetType == TYP_VOID)
{
#endif
}
-/*****************************************************************************
- *
- * Generate code for a single node in the tree.
- * Preconditions: All operands have been evaluated
- *
- */
-void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
-{
- regNumber targetReg = treeNode->gtRegNum;
- var_types targetType = treeNode->TypeGet();
- emitter* emit = getEmitter();
-
-#ifdef DEBUG
- // Validate that all the operands for the current node are consumed in order.
- // This is important because LSRA ensures that any necessary copies will be
- // handled correctly.
- lastConsumedNode = nullptr;
- if (compiler->verbose)
- {
- unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
- compiler->gtDispLIRNode(treeNode, "Generating: ");
- }
-#endif // DEBUG
-
- // Is this a node whose value is already in a register? LSRA denotes this by
- // setting the GTF_REUSE_REG_VAL flag.
- if (treeNode->IsReuseRegVal())
- {
- // For now, this is only used for constant nodes.
- assert((treeNode->OperGet() == GT_CNS_INT) || (treeNode->OperGet() == GT_CNS_DBL));
- JITDUMP(" TreeNode is marked ReuseReg\n");
- return;
- }
-
- // contained nodes are part of their parents for codegen purposes
- // ex : immediates, most LEAs
- if (treeNode->isContained())
- {
- return;
- }
-
- switch (treeNode->gtOper)
- {
- case GT_START_NONGC:
- getEmitter()->emitDisableGC();
- break;
-
- case GT_PROF_HOOK:
- // We should be seeing this only if profiler hook is needed
- noway_assert(compiler->compIsProfilerHookNeeded());
-
-#ifdef PROFILING_SUPPORTED
- // Right now this node is used only for tail calls. In future if
- // we intend to use it for Enter or Leave hooks, add a data member
- // to this node indicating the kind of profiler hook. For example,
- // helper number can be used.
- genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_TAILCALL);
-#endif // PROFILING_SUPPORTED
- break;
-
- case GT_LCLHEAP:
- genLclHeap(treeNode);
- break;
-
- case GT_CNS_INT:
- case GT_CNS_DBL:
- genSetRegToConst(targetReg, targetType, treeNode);
- genProduceReg(treeNode);
- break;
-
- case GT_NOT:
- case GT_NEG:
- genCodeForNegNot(treeNode);
- break;
-
- case GT_DIV:
- case GT_UDIV:
- genConsumeOperands(treeNode->AsOp());
-
- if (varTypeIsFloating(targetType))
- {
- // Floating point divide never raises an exception
- genCodeForBinary(treeNode);
- }
- else // an integer divide operation
- {
- GenTreePtr divisorOp = treeNode->gtGetOp2();
- emitAttr size = EA_ATTR(genTypeSize(genActualType(treeNode->TypeGet())));
-
- if (divisorOp->IsIntegralConst(0))
- {
- // We unconditionally throw a divide by zero exception
- genJumpToThrowHlpBlk(EJ_jmp, SCK_DIV_BY_ZERO);
-
- // We still need to call genProduceReg
- genProduceReg(treeNode);
- }
- else // the divisor is not the constant zero
- {
- regNumber divisorReg = divisorOp->gtRegNum;
-
- // Generate the require runtime checks for GT_DIV or GT_UDIV
- if (treeNode->gtOper == GT_DIV)
- {
- BasicBlock* sdivLabel = genCreateTempLabel();
-
- // Two possible exceptions:
- // (AnyVal / 0) => DivideByZeroException
- // (MinInt / -1) => ArithmeticException
- //
- bool checkDividend = true;
-
- // Do we have an immediate for the 'divisorOp'?
- //
- if (divisorOp->IsCnsIntOrI())
- {
- GenTreeIntConCommon* intConstTree = divisorOp->AsIntConCommon();
- ssize_t intConstValue = intConstTree->IconValue();
- assert(intConstValue != 0); // already checked above by IsIntegralConst(0))
- if (intConstValue != -1)
- {
- checkDividend = false; // We statically know that the dividend is not -1
- }
- }
- else // insert check for divison by zero
- {
- // Check if the divisor is zero throw a DivideByZeroException
- emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
- emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
- }
-
- if (checkDividend)
- {
- // Check if the divisor is not -1 branch to 'sdivLabel'
- emit->emitIns_R_I(INS_cmp, size, divisorReg, -1);
-
- emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
- inst_JMP(jmpNotEqual, sdivLabel);
- // If control flow continues past here the 'divisorReg' is known to be -1
-
- regNumber dividendReg = treeNode->gtGetOp1()->gtRegNum;
- // At this point the divisor is known to be -1
- //
- // Issue the 'adds zr, dividendReg, dividendReg' instruction
- // this will set both the Z and V flags only when dividendReg is MinInt
- //
- emit->emitIns_R_R_R(INS_adds, size, REG_ZR, dividendReg, dividendReg);
- inst_JMP(jmpNotEqual, sdivLabel); // goto sdiv if the Z flag is clear
- genJumpToThrowHlpBlk(EJ_vs, SCK_ARITH_EXCPN); // if the V flags is set throw
- // ArithmeticException
-
- genDefineTempLabel(sdivLabel);
- }
- genCodeForBinary(treeNode); // Generate the sdiv instruction
- }
- else // (treeNode->gtOper == GT_UDIV)
- {
- // Only one possible exception
- // (AnyVal / 0) => DivideByZeroException
- //
- // Note that division by the constant 0 was already checked for above by the
- // op2->IsIntegralConst(0) check
- //
- if (!divisorOp->IsCnsIntOrI())
- {
- // divisorOp is not a constant, so it could be zero
- //
- emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
- emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
- }
- genCodeForBinary(treeNode);
- }
- }
- }
- break;
-
- case GT_OR:
- case GT_XOR:
- case GT_AND:
- assert(varTypeIsIntegralOrI(treeNode));
- __fallthrough;
- case GT_ADD:
- case GT_SUB:
- case GT_MUL:
- genConsumeOperands(treeNode->AsOp());
- genCodeForBinary(treeNode);
- break;
-
- case GT_LSH:
- case GT_RSH:
- case GT_RSZ:
- case GT_ROR:
- genCodeForShift(treeNode);
- // genCodeForShift() calls genProduceReg()
- break;
-
- case GT_CAST:
- if (varTypeIsFloating(targetType) && varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double <--> double/float
- genFloatToFloatCast(treeNode);
- }
- else if (varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double --> int32/int64
- genFloatToIntCast(treeNode);
- }
- else if (varTypeIsFloating(targetType))
- {
- // Casts int32/uint32/int64/uint64 --> float/double
- genIntToFloatCast(treeNode);
- }
- else
- {
- // Casts int <--> int
- genIntToIntCast(treeNode);
- }
- // The per-case functions call genProduceReg()
- break;
-
- case GT_LCL_FLD_ADDR:
- case GT_LCL_VAR_ADDR:
- // Address of a local var. This by itself should never be allocated a register.
- // If it is worth storing the address in a register then it should be cse'ed into
- // a temp and that would be allocated a register.
- noway_assert(targetType == TYP_BYREF);
- noway_assert(!treeNode->InReg());
-
- inst_RV_TT(INS_lea, targetReg, treeNode, 0, EA_BYREF);
- genProduceReg(treeNode);
- break;
-
- case GT_LCL_FLD:
- genCodeForLclFld(treeNode->AsLclFld());
- break;
-
- case GT_LCL_VAR:
- genCodeForLclVar(treeNode->AsLclVar());
- break;
-
- case GT_STORE_LCL_FLD:
- genCodeForStoreLclFld(treeNode->AsLclFld());
- break;
-
- case GT_STORE_LCL_VAR:
- genCodeForStoreLclVar(treeNode->AsLclVar());
- break;
-
- case GT_RETFILT:
- // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in
- // the return register, if it's not already there. The processing is the same as GT_RETURN.
- if (targetType != TYP_VOID)
- {
- // For filters, the IL spec says the result is type int32. Further, the only specified legal values
- // are 0 or 1, with the use of other values "undefined".
- assert(targetType == TYP_INT);
- }
-
- __fallthrough;
-
- case GT_RETURN:
- genReturn(treeNode);
- break;
-
- case GT_LEA:
- // if we are here, it is the case where there is an LEA that cannot
- // be folded into a parent instruction
- genLeaInstruction(treeNode->AsAddrMode());
- break;
-
- case GT_IND:
- genConsumeAddress(treeNode->AsIndir()->Addr());
- emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(treeNode), targetReg, treeNode->AsIndir());
- genProduceReg(treeNode);
- break;
-
- case GT_MULHI:
- genCodeForMulHi(treeNode->AsOp());
- genProduceReg(treeNode);
- break;
-
- case GT_MOD:
- case GT_UMOD:
- // Integer MOD should have been morphed into a sequence of sub, mul, div in fgMorph.
- //
- // We shouldn't be seeing GT_MOD on float/double as it is morphed into a helper call by front-end.
- noway_assert(!"Codegen for GT_MOD/GT_UMOD");
- break;
-
- case GT_INTRINSIC:
- genIntrinsic(treeNode);
- break;
-
-#ifdef FEATURE_SIMD
- case GT_SIMD:
- genSIMDIntrinsic(treeNode->AsSIMD());
- break;
-#endif // FEATURE_SIMD
-
- case GT_CKFINITE:
- genCkfinite(treeNode);
- break;
-
- case GT_EQ:
- case GT_NE:
- case GT_LT:
- case GT_LE:
- case GT_GE:
- case GT_GT:
- genCodeForCompare(treeNode->AsOp());
- break;
-
- case GT_JTRUE:
- genCodeForJumpTrue(treeNode);
- break;
-
- case GT_RETURNTRAP:
- genCodeForReturnTrap(treeNode->AsOp());
- break;
-
- case GT_STOREIND:
- genCodeForStoreInd(treeNode->AsStoreInd());
- break;
-
- case GT_COPY:
- // This is handled at the time we call genConsumeReg() on the GT_COPY
- break;
-
- case GT_SWAP:
- genCodeForSwap(treeNode->AsOp());
- break;
-
- case GT_LIST:
- case GT_FIELD_LIST:
- case GT_ARGPLACE:
- // Nothing to do
- break;
-
- case GT_PUTARG_STK:
- genPutArgStk(treeNode->AsPutArgStk());
- break;
-
- case GT_PUTARG_REG:
- genPutArgReg(treeNode->AsOp());
- break;
-
- case GT_CALL:
- genCallInstruction(treeNode->AsCall());
- break;
-
- case GT_JMP:
- genJmpMethod(treeNode);
- break;
-
- case GT_LOCKADD:
- case GT_XCHG:
- case GT_XADD:
- genLockedInstructions(treeNode->AsOp());
- break;
-
- case GT_MEMORYBARRIER:
- instGen_MemoryBarrier();
- break;
-
- case GT_CMPXCHG:
- NYI("GT_CMPXCHG");
- break;
-
- case GT_RELOAD:
- // do nothing - reload is just a marker.
- // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
- // into the register specified in this node.
- break;
-
- case GT_NOP:
- break;
-
- case GT_NO_OP:
- if (treeNode->gtFlags & GTF_NO_OP_NO)
- {
- noway_assert(!"GTF_NO_OP_NO should not be set");
- }
- else
- {
- instGen(INS_nop);
- }
- break;
-
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
- genRangeCheck(treeNode);
- break;
-
- case GT_PHYSREG:
- if (targetReg != treeNode->AsPhysReg()->gtSrcReg)
- {
- inst_RV_RV(ins_Copy(targetType), targetReg, treeNode->AsPhysReg()->gtSrcReg, targetType);
-
- genTransferRegGCState(targetReg, treeNode->AsPhysReg()->gtSrcReg);
- }
- genProduceReg(treeNode);
- break;
-
- case GT_PHYSREGDST:
- break;
-
- case GT_NULLCHECK:
- {
- assert(!treeNode->gtOp.gtOp1->isContained());
- regNumber reg = genConsumeReg(treeNode->gtOp.gtOp1);
- emit->emitIns_R_R_I(INS_ldr, EA_4BYTE, REG_ZR, reg, 0);
- }
- break;
-
- case GT_CATCH_ARG:
-
- noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
-
- /* Catch arguments get passed in a register. genCodeForBBlist()
- would have marked it as holding a GC object, but not used. */
-
- noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
- genConsumeReg(treeNode);
- break;
-
- case GT_PINVOKE_PROLOG:
- noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
-
- // the runtime side requires the codegen here to be consistent
- emit->emitDisableRandomNops();
- break;
-
- case GT_LABEL:
- genPendingCallLabel = genCreateTempLabel();
- treeNode->gtLabel.gtLabBB = genPendingCallLabel;
-
- // For long address (default): `adrp + add` will be emitted.
- // For short address (proven later): `adr` will be emitted.
- emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
- break;
-
- case GT_STORE_OBJ:
- if (treeNode->OperIsCopyBlkOp())
- {
- assert(treeNode->AsObj()->gtGcPtrCount != 0);
- genCodeForCpObj(treeNode->AsObj());
- break;
- }
- __fallthrough;
-
- case GT_STORE_DYN_BLK:
- case GT_STORE_BLK:
- {
- GenTreeBlk* blkOp = treeNode->AsBlk();
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitDisableGC();
- }
- bool isCopyBlk = blkOp->OperIsCopyBlkOp();
-
- switch (blkOp->gtBlkOpKind)
- {
- case GenTreeBlk::BlkOpKindHelper:
- if (isCopyBlk)
- {
- genCodeForCpBlk(blkOp);
- }
- else
- {
- genCodeForInitBlk(blkOp);
- }
- break;
- case GenTreeBlk::BlkOpKindUnroll:
- if (isCopyBlk)
- {
- genCodeForCpBlkUnroll(blkOp);
- }
- else
- {
- genCodeForInitBlkUnroll(blkOp);
- }
- break;
- default:
- unreached();
- }
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitEnableGC();
- }
- }
- break;
-
- case GT_JMPTABLE:
- genJumpTable(treeNode);
- break;
-
- case GT_SWITCH_TABLE:
- genTableBasedSwitch(treeNode);
- break;
-
- case GT_ARR_INDEX:
- genCodeForArrIndex(treeNode->AsArrIndex());
- break;
-
- case GT_ARR_OFFSET:
- genCodeForArrOffset(treeNode->AsArrOffs());
- break;
-
- case GT_CLS_VAR_ADDR:
- NYI("GT_CLS_VAR_ADDR");
- break;
-
- case GT_IL_OFFSET:
- // Do nothing; these nodes are simply markers for debug info.
- break;
-
- default:
- {
-#ifdef DEBUG
- char message[256];
- _snprintf_s(message, _countof(message), _TRUNCATE, "Unimplemented node type %s\n",
- GenTree::NodeName(treeNode->OperGet()));
-#endif
- assert(!"Unknown node in codegen");
- }
- break;
- }
-}
-
/***********************************************************************************************
* Generate code for localloc
*/
genProduceReg(tree);
}
+//------------------------------------------------------------------------
+// genCodeForDivMod: Produce code for a GT_DIV/GT_UDIV node. We don't see MOD:
+// (1) integer MOD is morphed into a sequence of sub, mul, div in fgMorph;
+// (2) float/double MOD is morphed into a helper call by front-end.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForDivMod(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_DIV, GT_UDIV));
+
+ var_types targetType = tree->TypeGet();
+ emitter* emit = getEmitter();
+
+ genConsumeOperands(tree);
+
+ if (varTypeIsFloating(targetType))
+ {
+ // Floating point divide never raises an exception
+ genCodeForBinary(tree);
+ }
+ else // an integer divide operation
+ {
+ GenTreePtr divisorOp = tree->gtGetOp2();
+ emitAttr size = EA_ATTR(genTypeSize(genActualType(tree->TypeGet())));
+
+ if (divisorOp->IsIntegralConst(0))
+ {
+ // We unconditionally throw a divide by zero exception
+ genJumpToThrowHlpBlk(EJ_jmp, SCK_DIV_BY_ZERO);
+
+ // We still need to call genProduceReg
+ genProduceReg(tree);
+ }
+ else // the divisor is not the constant zero
+ {
+ regNumber divisorReg = divisorOp->gtRegNum;
+
+ // Generate the require runtime checks for GT_DIV or GT_UDIV
+ if (tree->gtOper == GT_DIV)
+ {
+ BasicBlock* sdivLabel = genCreateTempLabel();
+
+ // Two possible exceptions:
+ // (AnyVal / 0) => DivideByZeroException
+ // (MinInt / -1) => ArithmeticException
+ //
+ bool checkDividend = true;
+
+ // Do we have an immediate for the 'divisorOp'?
+ //
+ if (divisorOp->IsCnsIntOrI())
+ {
+ GenTreeIntConCommon* intConstTree = divisorOp->AsIntConCommon();
+ ssize_t intConstValue = intConstTree->IconValue();
+ assert(intConstValue != 0); // already checked above by IsIntegralConst(0))
+ if (intConstValue != -1)
+ {
+ checkDividend = false; // We statically know that the dividend is not -1
+ }
+ }
+ else // insert check for divison by zero
+ {
+ // Check if the divisor is zero throw a DivideByZeroException
+ emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
+ emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
+ genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
+ }
+
+ if (checkDividend)
+ {
+ // Check if the divisor is not -1 branch to 'sdivLabel'
+ emit->emitIns_R_I(INS_cmp, size, divisorReg, -1);
+
+ emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
+ inst_JMP(jmpNotEqual, sdivLabel);
+ // If control flow continues past here the 'divisorReg' is known to be -1
+
+ regNumber dividendReg = tree->gtGetOp1()->gtRegNum;
+ // At this point the divisor is known to be -1
+ //
+ // Issue the 'adds zr, dividendReg, dividendReg' instruction
+ // this will set both the Z and V flags only when dividendReg is MinInt
+ //
+ emit->emitIns_R_R_R(INS_adds, size, REG_ZR, dividendReg, dividendReg);
+ inst_JMP(jmpNotEqual, sdivLabel); // goto sdiv if the Z flag is clear
+ genJumpToThrowHlpBlk(EJ_vs, SCK_ARITH_EXCPN); // if the V flags is set throw
+ // ArithmeticException
+
+ genDefineTempLabel(sdivLabel);
+ }
+ genCodeForBinary(tree); // Generate the sdiv instruction
+ }
+ else // (tree->gtOper == GT_UDIV)
+ {
+ // Only one possible exception
+ // (AnyVal / 0) => DivideByZeroException
+ //
+ // Note that division by the constant 0 was already checked for above by the
+ // op2->IsIntegralConst(0) check
+ //
+ if (!divisorOp->IsCnsIntOrI())
+ {
+ // divisorOp is not a constant, so it could be zero
+ //
+ emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
+ emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
+ genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
+ }
+ genCodeForBinary(tree);
+ }
+ }
+ }
+}
+
// Generate code for InitBlk by performing a loop unroll
// Preconditions:
// a) Both the size and fill byte value are integer constants.
#include "emit.h"
//------------------------------------------------------------------------
+// genCodeForTreeNode Generate code for a single node in the tree.
+//
+// Preconditions:
+// All operands have been evaluated.
+//
+void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
+{
+ regNumber targetReg = treeNode->gtRegNum;
+ var_types targetType = treeNode->TypeGet();
+ emitter* emit = getEmitter();
+
+#ifdef DEBUG
+ // Validate that all the operands for the current node are consumed in order.
+ // This is important because LSRA ensures that any necessary copies will be
+ // handled correctly.
+ lastConsumedNode = nullptr;
+ if (compiler->verbose)
+ {
+ unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
+ compiler->gtDispLIRNode(treeNode, "Generating: ");
+ }
+#endif // DEBUG
+
+#ifdef _TARGET_ARM64_ // TODO-ARM: is this applicable to ARM32?
+ // Is this a node whose value is already in a register? LSRA denotes this by
+ // setting the GTF_REUSE_REG_VAL flag.
+ if (treeNode->IsReuseRegVal())
+ {
+ // For now, this is only used for constant nodes.
+ assert((treeNode->OperGet() == GT_CNS_INT) || (treeNode->OperGet() == GT_CNS_DBL));
+ JITDUMP(" TreeNode is marked ReuseReg\n");
+ return;
+ }
+#endif // _TARGET_ARM64_
+
+ // contained nodes are part of their parents for codegen purposes
+ // ex : immediates, most LEAs
+ if (treeNode->isContained())
+ {
+ return;
+ }
+
+ switch (treeNode->gtOper)
+ {
+#ifdef _TARGET_ARM64_
+
+ case GT_START_NONGC:
+ getEmitter()->emitDisableGC();
+ break;
+
+ case GT_PROF_HOOK:
+ // We should be seeing this only if profiler hook is needed
+ noway_assert(compiler->compIsProfilerHookNeeded());
+
+#ifdef PROFILING_SUPPORTED
+ // Right now this node is used only for tail calls. In future if
+ // we intend to use it for Enter or Leave hooks, add a data member
+ // to this node indicating the kind of profiler hook. For example,
+ // helper number can be used.
+ genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_TAILCALL);
+#endif // PROFILING_SUPPORTED
+ break;
+
+#endif // _TARGET_ARM64_
+
+ case GT_LCLHEAP:
+ genLclHeap(treeNode);
+ break;
+
+ case GT_CNS_INT:
+ case GT_CNS_DBL:
+ genSetRegToConst(targetReg, targetType, treeNode);
+ genProduceReg(treeNode);
+ break;
+
+ case GT_NOT:
+ case GT_NEG:
+ genCodeForNegNot(treeNode);
+ break;
+
+ case GT_MOD:
+ case GT_UMOD:
+ case GT_DIV:
+ case GT_UDIV:
+ genCodeForDivMod(treeNode->AsOp());
+ break;
+
+ case GT_OR:
+ case GT_XOR:
+ case GT_AND:
+ assert(varTypeIsIntegralOrI(treeNode));
+
+ __fallthrough;
+
+#ifdef _TARGET_ARM_
+ case GT_ADD_LO:
+ case GT_ADD_HI:
+ case GT_SUB_LO:
+ case GT_SUB_HI:
+#endif // _TARGET_ARM_
+
+ case GT_ADD:
+ case GT_SUB:
+ case GT_MUL:
+ genConsumeOperands(treeNode->AsOp());
+ genCodeForBinary(treeNode);
+ break;
+
+ case GT_LSH:
+ case GT_RSH:
+ case GT_RSZ:
+ case GT_ROR:
+ genCodeForShift(treeNode);
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_LSH_HI:
+ case GT_RSH_LO:
+ genCodeForShiftLong(treeNode);
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_CAST:
+ genCodeForCast(treeNode->AsOp());
+ break;
+
+ case GT_LCL_FLD_ADDR:
+ case GT_LCL_VAR_ADDR:
+ genCodeForLclAddr(treeNode);
+ break;
+
+ case GT_LCL_FLD:
+ genCodeForLclFld(treeNode->AsLclFld());
+ break;
+
+ case GT_LCL_VAR:
+ genCodeForLclVar(treeNode->AsLclVar());
+ break;
+
+ case GT_STORE_LCL_FLD:
+ genCodeForStoreLclFld(treeNode->AsLclFld());
+ break;
+
+ case GT_STORE_LCL_VAR:
+ genCodeForStoreLclVar(treeNode->AsLclVar());
+ break;
+
+ case GT_RETFILT:
+ case GT_RETURN:
+ genReturn(treeNode);
+ break;
+
+ case GT_LEA:
+ // if we are here, it is the case where there is an LEA that cannot
+ // be folded into a parent instruction
+ genLeaInstruction(treeNode->AsAddrMode());
+ break;
+
+ case GT_IND:
+ genCodeForIndir(treeNode->AsIndir());
+ break;
+
+#ifdef _TARGET_ARM64_
+
+ case GT_MULHI:
+ genCodeForMulHi(treeNode->AsOp());
+ break;
+
+ case GT_CKFINITE:
+ genCkfinite(treeNode);
+ break;
+
+ case GT_SWAP:
+ genCodeForSwap(treeNode->AsOp());
+ break;
+
+ case GT_JMP:
+ genJmpMethod(treeNode);
+ break;
+
+#endif // _TARGET_ARM64_
+
+ case GT_INTRINSIC:
+ genIntrinsic(treeNode);
+ break;
+
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ genSIMDIntrinsic(treeNode->AsSIMD());
+ break;
+#endif // FEATURE_SIMD
+
+ case GT_EQ:
+ case GT_NE:
+ case GT_LT:
+ case GT_LE:
+ case GT_GE:
+ case GT_GT:
+ genCodeForCompare(treeNode->AsOp());
+ break;
+
+ case GT_JTRUE:
+ genCodeForJumpTrue(treeNode);
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_JCC:
+ genCodeForJcc(treeNode->AsJumpCC());
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_RETURNTRAP:
+ genCodeForReturnTrap(treeNode->AsOp());
+ break;
+
+ case GT_STOREIND:
+ genCodeForStoreInd(treeNode->AsStoreInd());
+ break;
+
+ case GT_COPY:
+ // This is handled at the time we call genConsumeReg() on the GT_COPY
+ break;
+
+ case GT_LIST:
+ case GT_FIELD_LIST:
+ case GT_ARGPLACE:
+ // Nothing to do
+ break;
+
+ case GT_PUTARG_STK:
+ genPutArgStk(treeNode->AsPutArgStk());
+ break;
+
+ case GT_PUTARG_REG:
+ genPutArgReg(treeNode->AsOp());
+ break;
+
+ case GT_CALL:
+ genCallInstruction(treeNode->AsCall());
+ break;
+
+ case GT_LOCKADD:
+ case GT_XCHG:
+ case GT_XADD:
+ genLockedInstructions(treeNode->AsOp());
+ break;
+
+ case GT_MEMORYBARRIER:
+ instGen_MemoryBarrier();
+ break;
+
+ case GT_CMPXCHG:
+ NYI("GT_CMPXCHG");
+ break;
+
+ case GT_RELOAD:
+ // do nothing - reload is just a marker.
+ // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
+ // into the register specified in this node.
+ break;
+
+ case GT_NOP:
+ break;
+
+ case GT_NO_OP:
+ if (treeNode->gtFlags & GTF_NO_OP_NO)
+ {
+ noway_assert(!"GTF_NO_OP_NO should not be set");
+ }
+ else
+ {
+ instGen(INS_nop);
+ }
+ break;
+
+ case GT_ARR_BOUNDS_CHECK:
+#ifdef FEATURE_SIMD
+ case GT_SIMD_CHK:
+#endif // FEATURE_SIMD
+ genRangeCheck(treeNode);
+ break;
+
+ case GT_PHYSREG:
+ genCodeForPhysReg(treeNode->AsPhysReg());
+ break;
+
+ case GT_PHYSREGDST:
+ break;
+
+ case GT_NULLCHECK:
+ genCodeForNullCheck(treeNode->AsOp());
+ break;
+
+ case GT_CATCH_ARG:
+
+ noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
+
+ /* Catch arguments get passed in a register. genCodeForBBlist()
+ would have marked it as holding a GC object, but not used. */
+
+ noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
+ genConsumeReg(treeNode);
+ break;
+
+ case GT_PINVOKE_PROLOG:
+ noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
+
+ // the runtime side requires the codegen here to be consistent
+ emit->emitDisableRandomNops();
+ break;
+
+ case GT_LABEL:
+ genPendingCallLabel = genCreateTempLabel();
+ treeNode->gtLabel.gtLabBB = genPendingCallLabel;
+ emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
+ break;
+
+ case GT_STORE_OBJ:
+ case GT_STORE_DYN_BLK:
+ case GT_STORE_BLK:
+ genCodeForStoreBlk(treeNode->AsBlk());
+ break;
+
+ case GT_JMPTABLE:
+ genJumpTable(treeNode);
+ break;
+
+ case GT_SWITCH_TABLE:
+ genTableBasedSwitch(treeNode);
+ break;
+
+ case GT_ARR_INDEX:
+ genCodeForArrIndex(treeNode->AsArrIndex());
+ break;
+
+ case GT_ARR_OFFSET:
+ genCodeForArrOffset(treeNode->AsArrOffs());
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_CLS_VAR_ADDR:
+ emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
+ genProduceReg(treeNode);
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_IL_OFFSET:
+ // Do nothing; these nodes are simply markers for debug info.
+ break;
+
+ default:
+ {
+#ifdef DEBUG
+ char message[256];
+ _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
+ GenTree::NodeName(treeNode->OperGet()));
+ NYIRAW(message);
+#else
+ NYI("unimplemented node");
+#endif
+ }
+ break;
+ }
+}
+
+//------------------------------------------------------------------------
// genSetRegToIcon: Generate code that will set the given register to the integer constant.
//
void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags)
//
void CodeGen::genIntrinsic(GenTreePtr treeNode)
{
+ assert(treeNode->OperIs(GT_INTRINSIC));
+
// Both operand and its result must be of the same floating point type.
GenTreePtr srcNode = treeNode->gtOp.gtOp1;
assert(varTypeIsFloating(srcNode));
// genPutArgReg - generate code for a GT_PUTARG_REG node
//
// Arguments
-// treeNode - the GT_PUTARG_REG node
+// tree - the GT_PUTARG_REG node
//
// Return value:
// None
genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
+//---------------------------------------------------------------------
+// genCodeForPhysReg - generate code for a GT_PHYSREG node
+//
+// Arguments
+// tree - the GT_PHYSREG node
+//
+// Return value:
+// None
+//
+void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree)
+{
+ assert(tree->OperIs(GT_PHYSREG));
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ if (targetReg != tree->gtSrcReg)
+ {
+ inst_RV_RV(ins_Copy(targetType), targetReg, tree->gtSrcReg, targetType);
+ genTransferRegGCState(targetReg, tree->gtSrcReg);
+ }
+
+ genProduceReg(tree);
+}
+
+//---------------------------------------------------------------------
+// genCodeForNullCheck - generate code for a GT_NULLCHECK node
+//
+// Arguments
+// tree - the GT_NULLCHECK node
+//
+// Return value:
+// None
+//
+void CodeGen::genCodeForNullCheck(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_NULLCHECK));
+ assert(!tree->gtOp1->isContained());
+ regNumber addrReg = genConsumeReg(tree->gtOp1);
+
+#ifdef _TARGET_ARM64_
+ regNumber targetReg = REG_ZR;
+#else
+ regNumber targetReg = tree->gtRegNum;
+#endif
+
+ getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
+}
+
//------------------------------------------------------------------------
// genOffsetOfMDArrayLowerBound: Returns the offset from the Array object to the
// lower bound for the given dimension.
}
//------------------------------------------------------------------------
+// genCodeForCast: Generates the code for GT_CAST.
+//
+// Arguments:
+// tree - the GT_CAST node.
+//
+void CodeGen::genCodeForCast(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_CAST));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ // Cast is never contained (?)
+ noway_assert(targetReg != REG_NA);
+
+ if (varTypeIsFloating(targetType) && varTypeIsFloating(tree->gtOp1))
+ {
+ // Casts float/double <--> double/float
+ genFloatToFloatCast(tree);
+ }
+ else if (varTypeIsFloating(tree->gtOp1))
+ {
+ // Casts float/double --> int32/int64
+ genFloatToIntCast(tree);
+ }
+ else if (varTypeIsFloating(targetType))
+ {
+ // Casts int32/uint32/int64/uint64 --> float/double
+ genIntToFloatCast(tree);
+ }
+ else
+ {
+ // Casts int <--> int
+ genIntToIntCast(tree);
+ }
+ // The per-case functions call genProduceReg()
+}
+
+//------------------------------------------------------------------------
+// genCodeForLclAddr: Generates the code for GT_LCL_FLD_ADDR/GT_LCL_VAR_ADDR.
+//
+// Arguments:
+// tree - the node.
+//
+void CodeGen::genCodeForLclAddr(GenTree* tree)
+{
+ assert(tree->OperIs(GT_LCL_FLD_ADDR, GT_LCL_VAR_ADDR));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ // Address of a local var. This by itself should never be allocated a register.
+ // If it is worth storing the address in a register then it should be cse'ed into
+ // a temp and that would be allocated a register.
+ noway_assert(targetType == TYP_BYREF);
+ noway_assert(!tree->InReg());
+
+ inst_RV_TT(INS_lea, targetReg, tree, 0, EA_BYREF);
+ genProduceReg(tree);
+}
+
+//------------------------------------------------------------------------
// genCodeForLclFld: Produce code for a GT_LCL_FLD node.
//
// Arguments:
//
void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
{
+ assert(tree->OperIs(GT_LCL_FLD));
+
var_types targetType = tree->TypeGet();
regNumber targetReg = tree->gtRegNum;
emitter* emit = getEmitter();
genProduceReg(tree);
}
+//------------------------------------------------------------------------
+// genCodeForIndir: Produce code for a GT_IND node.
+//
+// Arguments:
+// tree - the GT_IND node
+//
+void CodeGen::genCodeForIndir(GenTreeIndir* tree)
+{
+ assert(tree->OperIs(GT_IND));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+ emitter* emit = getEmitter();
+
+ genConsumeAddress(tree->Addr());
+ emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(tree), targetReg, tree);
+ genProduceReg(tree);
+}
+
// Generate code for a CpBlk node by the means of the VM memcpy helper call
// Preconditions:
// a) The size argument of the CpBlk is not an integer constant
}
}
+//------------------------------------------------------------------------
+// genCodeForStoreBlk: Produce code for a GT_STORE_OBJ/GT_STORE_DYN_BLK/GT_STORE_BLK node.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
+{
+ assert(blkOp->OperIs(GT_STORE_OBJ, GT_STORE_DYN_BLK, GT_STORE_BLK));
+
+#ifdef _TARGET_ARM_
+ NYI_IF(blkOp->OperIs(GT_STORE_OBJ), "GT_STORE_OBJ");
+#endif // _TARGET_ARM_
+
+#ifndef _TARGET_ARM_ // NYI for ARM
+ if (blkOp->OperIs(GT_STORE_OBJ) && blkOp->OperIsCopyBlkOp())
+ {
+ assert(blkOp->AsObj()->gtGcPtrCount != 0);
+ genCodeForCpObj(blkOp->AsObj());
+ return;
+ }
+#endif // !_TARGET_ARM_
+
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ getEmitter()->emitDisableGC();
+ }
+ bool isCopyBlk = blkOp->OperIsCopyBlkOp();
+
+ switch (blkOp->gtBlkOpKind)
+ {
+ case GenTreeBlk::BlkOpKindHelper:
+ if (isCopyBlk)
+ {
+ genCodeForCpBlk(blkOp);
+ }
+ else
+ {
+ genCodeForInitBlk(blkOp);
+ }
+ break;
+
+ case GenTreeBlk::BlkOpKindUnroll:
+ if (isCopyBlk)
+ {
+ genCodeForCpBlkUnroll(blkOp);
+ }
+ else
+ {
+ genCodeForInitBlkUnroll(blkOp);
+ }
+ break;
+
+ default:
+ unreached();
+ }
+
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ getEmitter()->emitEnableGC();
+ }
+}
+
#endif // _TARGET_ARMARCH_
#endif // !LEGACY_BACKEND
var_types storeType = varDsc->lvaArgType();
regNumber argReg = varDsc->lvArgReg;
- getEmitter()->emitIns_S_R(ins_Store(storeType), emitTypeSize(storeType), argReg, varNum, 0);
+
+ instruction store_ins = ins_Store(storeType);
+
+#ifdef FEATURE_SIMD
+ if ((storeType == TYP_SIMD8) && genIsValidIntReg(argReg))
+ {
+ store_ins = INS_mov;
+ }
+#endif // FEATURE_SIMD
+
+ getEmitter()->emitIns_S_R(store_ins, emitTypeSize(storeType), argReg, varNum, 0);
}
}
var_types loadType = varDsc->lvaArgType();
regNumber argReg = varDsc->lvArgReg;
- getEmitter()->emitIns_R_S(ins_Load(loadType), emitTypeSize(loadType), argReg, varNum, 0);
+
+ instruction load_ins = ins_Load(loadType);
+
+#ifdef FEATURE_SIMD
+ if ((loadType == TYP_SIMD8) && genIsValidIntReg(argReg))
+ {
+ load_ins = INS_mov;
+ }
+#endif // FEATURE_SIMD
+
+ getEmitter()->emitIns_R_S(load_ins, emitTypeSize(loadType), argReg, varNum, 0);
#if FEATURE_VARARG
if (compiler->info.compIsVarArgs && varTypeIsFloating(loadType))
genConsumeAddress(tree->AsIndir()->Addr());
}
#ifdef _TARGET_XARCH_
- else if (tree->OperGet() == GT_LCL_VAR)
+ else if (tree->OperIsLocalRead())
{
// A contained lcl var must be living on stack and marked as reg optional, or not be a
// register candidate.
#ifndef LEGACY_BACKEND // Not necessary (it's this way in the #include location), but helpful to IntelliSense
void genSetRegToConst(regNumber targetReg, var_types targetType, GenTreePtr tree);
-
void genCodeForTreeNode(GenTreePtr treeNode);
-
void genCodeForBinary(GenTreePtr treeNode);
#if defined(_TARGET_X86_)
#endif // _TARGET_X86_
void genCodeForDivMod(GenTreeOp* treeNode);
-
void genCodeForMulHi(GenTreeOp* treeNode);
-
void genLeaInstruction(GenTreeAddrMode* lea);
-
void genSetRegToCond(regNumber dstReg, GenTreePtr tree);
#if !defined(_TARGET_64BIT_)
#endif
void genIntToIntCast(GenTreePtr treeNode);
-
void genFloatToFloatCast(GenTreePtr treeNode);
-
void genFloatToIntCast(GenTreePtr treeNode);
-
void genIntToFloatCast(GenTreePtr treeNode);
-
void genCkfinite(GenTreePtr treeNode);
-
void genCodeForCompare(GenTreeOp* tree);
-
void genIntrinsic(GenTreePtr treeNode);
-
void genPutArgStk(GenTreePutArgStk* treeNode);
-
void genPutArgReg(GenTreeOp* tree);
#if defined(_TARGET_XARCH_)
#endif // _TARGET_XARCH_ || _TARGET_ARM64_
void genCompareFloat(GenTreePtr treeNode);
-
void genCompareInt(GenTreePtr treeNode);
#if !defined(_TARGET_64BIT_)
void genSIMDIntrinsicShuffleSSE2(GenTreeSIMD* simdNode);
void genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode);
void genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode);
-
void genSIMDIntrinsic(GenTreeSIMD* simdNode);
void genSIMDCheck(GenTree* treeNode);
#endif // !defined(_TARGET_64BIT_)
void genProduceReg(GenTree* tree);
-
void genUnspillRegIfNeeded(GenTree* tree);
-
regNumber genConsumeReg(GenTree* tree);
-
void genCopyRegIfNeeded(GenTree* tree, regNumber needReg);
void genConsumeRegAndCopy(GenTree* tree, regNumber needReg);
}
void genRegCopy(GenTreePtr tree);
-
void genTransferRegGCState(regNumber dst, regNumber src);
-
void genConsumeAddress(GenTree* addr);
-
void genConsumeAddrMode(GenTreeAddrMode* mode);
-
void genSetBlockSize(GenTreeBlk* blkNode, regNumber sizeReg);
void genConsumeBlockSrc(GenTreeBlk* blkNode);
void genSetBlockSrc(GenTreeBlk* blkNode, regNumber srcReg);
#endif // FEATURE_PUT_STRUCT_ARG_STK
void genConsumeRegs(GenTree* tree);
-
void genConsumeOperands(GenTreeOp* tree);
-
void genEmitGSCookieCheck(bool pushReg);
-
void genSetRegToIcon(regNumber reg, ssize_t val, var_types type = TYP_INT, insFlags flags = INS_FLAGS_DONT_CARE);
-
void genCodeForShift(GenTreePtr tree);
#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
void genCodeForShiftRMW(GenTreeStoreInd* storeInd);
#endif // _TARGET_XARCH_
+void genCodeForCast(GenTreeOp* tree);
+void genCodeForLclAddr(GenTree* tree);
+void genCodeForIndir(GenTreeIndir* tree);
void genCodeForNegNot(GenTree* tree);
-
void genCodeForLclVar(GenTreeLclVar* tree);
-
void genCodeForLclFld(GenTreeLclFld* tree);
-
void genCodeForStoreLclFld(GenTreeLclFld* tree);
-
void genCodeForStoreLclVar(GenTreeLclVar* tree);
-
void genCodeForReturnTrap(GenTreeOp* tree);
-
+void genCodeForJcc(GenTreeJumpCC* tree);
void genCodeForStoreInd(GenTreeStoreInd* tree);
-
void genCodeForSwap(GenTreeOp* tree);
-
void genCodeForCpObj(GenTreeObj* cpObjNode);
-
void genCodeForCpBlk(GenTreeBlk* cpBlkNode);
-
void genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode);
-
void genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode);
+void genCodeForPhysReg(GenTreePhysReg* tree);
+void genCodeForNullCheck(GenTreeOp* tree);
void genAlignStackBeforeCall(GenTreePutArgStk* putArgStk);
void genAlignStackBeforeCall(GenTreeCall* call);
#endif // FEATURE_PUT_STRUCT_ARG_STK
void genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset);
-
void genCodeForStoreOffset(instruction ins, emitAttr size, regNumber src, GenTree* base, unsigned offset);
#ifdef _TARGET_ARM64_
void genCodeForLoadPairOffset(regNumber dst, regNumber dst2, GenTree* base, unsigned offset);
-
void genCodeForStorePairOffset(regNumber src, regNumber src2, GenTree* base, unsigned offset);
#endif // _TARGET_ARM64_
void genCodeForStoreBlk(GenTreeBlk* storeBlkNode);
-
void genCodeForInitBlk(GenTreeBlk* initBlkNode);
-
void genCodeForInitBlkRepStos(GenTreeBlk* initBlkNode);
-
void genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode);
-
void genJumpTable(GenTree* tree);
-
void genTableBasedSwitch(GenTree* tree);
-
void genCodeForArrIndex(GenTreeArrIndex* treeNode);
-
void genCodeForArrOffset(GenTreeArrOffs* treeNode);
-
instruction genGetInsForOper(genTreeOps oper, var_types type);
-
void genStoreInd(GenTreePtr node);
-
bool genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarrierForm, GenTree* addr, GenTree* data);
-
void genCallInstruction(GenTreeCall* call);
-
void genJmpMethod(GenTreePtr jmp);
-
BasicBlock* genCallFinally(BasicBlock* block);
-
void genCodeForJumpTrue(GenTreePtr tree);
#if FEATURE_EH_FUNCLETS
bool isStructReturn(GenTreePtr treeNode);
void genStructReturn(GenTreePtr treeNode);
-// Codegen for GT_RETURN.
void genReturn(GenTreePtr treeNode);
void genLclHeap(GenTreePtr tree);
GenTreePtr op1 = treeNode->gtGetOp1();
genConsumeRegs(op1);
emit->emitInsBinary(ins_Store(targetType), emitTypeSize(treeNode), treeNode, op1);
+
+ genUpdateLife(treeNode);
}
break;
gtNewIconNode(offsetof(CORINFO_String, stringLen), TYP_I_IMPL));
op1 = gtNewOperNode(GT_IND, TYP_INT, op1);
}
+
+ // Getting the length of a null string should throw
+ op1->gtFlags |= GTF_EXCEPT;
+
retNode = op1;
break;
store->gtOp.gtOp1 = call;
store->gtFlags |= GTF_VAR_DEF;
+ GenTree* const insertionPoint = firstBlockRange.FirstNonPhiOrCatchArgNode();
+
comp->fgMorphTree(store);
- firstBlockRange.InsertAtEnd(LIR::SeqTree(comp, store));
+ firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, store));
DISPTREERANGE(firstBlockRange, store);
#if !defined(_TARGET_X86_) && !defined(_TARGET_ARM_)
GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar, callFrameInfo.offsetOfCallSiteSP);
storeSP->gtOp1 = PhysReg(REG_SPBASE);
- firstBlockRange.InsertAtEnd(LIR::SeqTree(comp, storeSP));
+ firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeSP));
DISPTREERANGE(firstBlockRange, storeSP);
#endif // !defined(_TARGET_X86_) && !defined(_TARGET_ARM_)
callFrameInfo.offsetOfCalleeSavedFP);
storeFP->gtOp1 = PhysReg(REG_FPBASE);
- firstBlockRange.InsertAtEnd(LIR::SeqTree(comp, storeFP));
+ firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeFP));
DISPTREERANGE(firstBlockRange, storeFP);
#endif // !defined(_TARGET_ARM_)
// Push a frame - if we are NOT in an IL stub, this is done right before the call
// The init routine sets InlinedCallFrame's m_pNext, so we just set the thead's top-of-stack
GenTree* frameUpd = CreateFrameLinkUpdate(PushFrame);
- firstBlockRange.InsertAtEnd(LIR::SeqTree(comp, frameUpd));
+ firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, frameUpd));
DISPTREERANGE(firstBlockRange, frameUpd);
}
#endif // _TARGET_64BIT_
#define RBM_CALLEE_SAVED (RBM_INT_CALLEE_SAVED | RBM_FLT_CALLEE_SAVED)
#define RBM_CALLEE_TRASH (RBM_INT_CALLEE_TRASH | RBM_FLT_CALLEE_TRASH)
- #define RBM_CALLEE_TRASH_NOGC (RBM_R12|RBM_R13|RBM_R14|RBM_R15)
+ #define RBM_CALLEE_TRASH_NOGC (RBM_R12|RBM_R13|RBM_R14|RBM_R15|RBM_IP1)
#define REG_DEFAULT_HELPER_CALL_TARGET REG_R12
#define RBM_ALLINT (RBM_INT_CALLEE_SAVED | RBM_INT_CALLEE_TRASH)
<data name="ArgumentOutOfRange_ArrayLBAndLength" xml:space="preserve">
<value>Higher indices will exceed Int32.MaxValue because of large lower bound and/or length.</value>
</data>
- <data name="ArgumentOutOfRange_ArrayListInsert" xml:space="preserve">
- <value>Insertion index was out of range. Must be non-negative and less than or equal to size.</value>
- </data>
<data name="ArgumentOutOfRange_BadHourMinuteSecond" xml:space="preserve">
<value>Hour, Minute, and Second parameters describe an un-representable DateTime.</value>
</data>
<AssemblyInfoLines Include="[assembly: System.Security.AllowPartiallyTrustedCallers]" />
<AssemblyInfoLines Include="[assembly: System.Runtime.InteropServices.ComVisible(false)]" />
<AssemblyInfoLines Include="[assembly: System.Resources.NeutralResourcesLanguage("en-US")]" />
+ <AssemblyInfoLines Include="[assembly: System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute(System.Runtime.InteropServices.DllImportSearchPath.AssemblyDirectory | System.Runtime.InteropServices.DllImportSearchPath.System32)]" />
</ItemGroup>
<!--
Helper Paths
<Compile Include="$(BclSourcesRoot)\System\Runtime\GcSettings.cs" />
</ItemGroup>
<ItemGroup>
- <Compile Include="$(BclSourcesRoot)\System\Collections\ArrayList.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Comparer.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\CompatibleComparer.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\ListDictionaryInternal.cs" />
<Compile Include="$(BclSourcesRoot)\System\MissingMemberException.cs" />
<Compile Include="$(BclSourcesRoot)\System\NonSerializedAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Number.cs" />
- <Compile Include="$(BclSourcesRoot)\System\OperatingSystem.cs" />
- <Compile Include="$(BclSourcesRoot)\System\OperationCanceledException.cs" />
<Compile Include="$(BclSourcesRoot)\System\ParseNumbers.cs" />
- <Compile Include="$(BclSourcesRoot)\System\PlatformID.cs" />
<Compile Include="$(BclSourcesRoot)\System\ResId.cs" />
<Compile Include="$(BclSourcesRoot)\System\RtType.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\GregorianCalendar.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\GregorianCalendarHelper.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\IdnMapping.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\NumberFormatInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\RegionInfo.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\SortKey.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\StringInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\TextElementEnumerator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\TextInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\TimeSpanFormat.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Monitor.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Mutex.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Overlapped.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Threading\PinnableBufferCache.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Semaphore.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\Thread.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\ThreadInterruptedException.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\Directory.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\SearchOption.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\DriveNotFoundException.cs" />
- <Compile Include="$(BclSourcesRoot)\System\IO\EncodingCache.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\File.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileLoadException.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileNotFoundException.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\IOException.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\MemoryStream.cs" />
- <Compile Include="$(BclSourcesRoot)\System\IO\PinnedBufferMemoryStream.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\Stream.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\UnmanagedMemoryAccessor.cs" />
- <Compile Include="$(BclSourcesRoot)\System\IO\UnmanagedMemoryStream.cs" />
- <Compile Include="$(BclSourcesRoot)\System\IO\UnmanagedMemoryStreamWrapper.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Security\DynamicSecurityMethodAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Serialization\SerializationInfo.cs" />
</ItemGroup>
<ItemGroup>
- <Compile Include="$(BclSourcesRoot)\System\Runtime\Remoting\ObjectHandle.cs" />
- </ItemGroup>
- <ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Text\DecoderNLS.cs" />
<Compile Include="$(BclSourcesRoot)\System\Text\DecoderBestFitFallback.cs" />
<Compile Include="$(BclSourcesRoot)\System\Text\DecoderExceptionFallback.cs" />
<Compile Include="$(BclSourcesRoot)\System\Numerics\Hashing\HashHelpers.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
- <Compile Include="$(BclSourcesRoot)\Interop\Unix\System.Globalization.Native\Interop.Collation.cs" />
<Compile Include="$(BclSourcesRoot)\Interop\Unix\System.Globalization.Native\Interop.ICU.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Diagnostics\Debug.Unix.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\CalendarData.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\CompareInfo.Unix.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\CultureData.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\CultureInfo.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\EncodingTable.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\EncodingDataItem.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.Unix.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\HijriCalendar.Unix.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\IdnMapping.Unix.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\JapaneseCalendar.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\TextInfo.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileSystemEnumerable.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\TextReader.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\CultureData.Windows.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\CultureInfo.Windows.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.Windows.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\HijriCalendar.Win32.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\IdnMapping.Windows.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\JapaneseCalendar.Win32.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\TextInfo.Windows.cs" />
<Compile Include="$(BclSourcesRoot)\System\Text\Normalization.Windows.cs" />
<Compile Include="$(BclSourcesRoot)\System\TimeZoneInfo.Win32.cs" />
<Compile Include="$(CommonPath)\Preprocessed\AssemblyRefs.g.cs" />
<!-- These files are shared with other framework components and don't live the same folder as the rest of them-->
<Compile Include="$(CommonPath)\NotImplemented.cs" />
- <Compile Include="$(CommonPath)\PinnableBufferCache.cs" />
<Compile Include="$(CommonPath)\System\SR.cs" />
<!-- Include Internals visible to file in the compilation -->
<Compile Include="$(BclSourcesRoot)\mscorlib.Friends.cs" />
// See the LICENSE file in the project root for more information.
using System;
-using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
protected override bool ReleaseHandle()
{
- Debug.Assert(!GlobalizationMode.Invariant);
-
CloseSortHandle(handle);
SetHandle(IntPtr.Zero);
return true;
internal static extern bool GetUserPreferredUILanguages(uint dwFlags, out uint pulNumLanguages, char [] pwszLanguagesBuffer, ref uint pcchLanguagesBuffer);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
- internal static extern int GetLocaleInfoEx(string lpLocaleName, uint LCType, IntPtr lpLCData, int cchData);
+ internal static extern int GetLocaleInfoEx(string lpLocaleName, uint LCType, void* lpLCData, int cchData);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
- internal extern static bool EnumSystemLocalesEx(EnumLocalesProcEx lpLocaleEnumProcEx, uint dwFlags, IntPtr lParam, IntPtr reserved);
+ internal extern static bool EnumSystemLocalesEx(EnumLocalesProcEx lpLocaleEnumProcEx, uint dwFlags, void* lParam, IntPtr reserved);
- internal delegate BOOL EnumLocalesProcEx(IntPtr lpLocaleString, uint dwFlags, IntPtr lParam);
+ internal delegate BOOL EnumLocalesProcEx(char* lpLocaleString, uint dwFlags, void* lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal extern static int ResolveLocaleName(string lpNameToResolve, char* lpLocaleName, int cchLocaleName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
- internal extern static bool EnumTimeFormatsEx(EnumTimeFormatsProcEx lpTimeFmtEnumProcEx, string lpLocaleName, uint dwFlags, IntPtr lParam);
+ internal extern static bool EnumTimeFormatsEx(EnumTimeFormatsProcEx lpTimeFmtEnumProcEx, string lpLocaleName, uint dwFlags, void* lParam);
- internal delegate BOOL EnumTimeFormatsProcEx(IntPtr lpTimeFormatString, IntPtr lParam);
+ internal delegate BOOL EnumTimeFormatsProcEx(char* lpTimeFormatString, void* lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal extern static int GetCalendarInfoEx(string lpLocaleName, uint Calendar, IntPtr lpReserved, uint CalType, IntPtr lpCalData, int cchData, out int lpValue);
internal extern static int GetCalendarInfoEx(string lpLocaleName, uint Calendar, IntPtr lpReserved, uint CalType, IntPtr lpCalData, int cchData, IntPtr lpValue);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
- internal extern static bool EnumCalendarInfoExEx(EnumCalendarInfoProcExEx pCalInfoEnumProcExEx, string lpLocaleName, uint Calendar, string lpReserved, uint CalType, IntPtr lParam);
+ internal extern static bool EnumCalendarInfoExEx(EnumCalendarInfoProcExEx pCalInfoEnumProcExEx, string lpLocaleName, uint Calendar, string lpReserved, uint CalType, void* lParam);
- internal delegate BOOL EnumCalendarInfoProcExEx(IntPtr lpCalendarInfoString, uint Calendar, IntPtr lpReserved, IntPtr lParam);
+ internal delegate BOOL EnumCalendarInfoProcExEx(char* lpCalendarInfoString, uint Calendar, IntPtr lpReserved, void* lParam);
[StructLayout(LayoutKind.Sequential)]
- internal struct NlsVersionInfoEx
+ internal struct NlsVersionInfoEx
{
internal int dwNLSVersionInfoSize;
internal int dwNLSVersion;
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JulianCalendar.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\KoreanCalendar.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\KoreanLunisolarCalendar.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\NumberFormatInfo.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\NumberStyles.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\PersianCalendar.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\SortKey.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\SortVersion.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\StringInfo.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TaiwanCalendar.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TaiwanLunisolarCalendar.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ThaiBuddhistCalendar.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\InvalidProgramException.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\InvalidTimeZoneException.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DirectoryNotFoundException.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\EncodingCache.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\EndOfStreamException.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Error.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileAccess.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathTooLongException.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\PinnedBufferMemoryStream.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\SeekOrigin.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\StreamHelpers.CopyValidation.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryStream.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryStreamWrapper.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IObservable.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IObserver.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IProgress.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\NullReferenceException.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\ObjectDisposedException.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\ObsoleteAttribute.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\OperationCanceledException.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\OverflowException.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\ParamArrayAttribute.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\ParamsArray.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempFileNameW.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempPathW.cs"/>
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Globalization.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Globalization.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LockFile.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.OutputDebugString.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysFreeString.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysStringLen.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" Condition="'$(IsProjectNLibrary)' != 'true' and '$(EnableDummyGlobalizationImplementation)' != 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.WinRT.cs" Condition="'$(IsProjectNLibrary)' == 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Win32.cs" Condition="'$(IsProjectNLibrary)' != 'true' and '$(EnableDummyGlobalizationImplementation)' != 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.WinRT.cs" Condition="'$(IsProjectNLibrary)' == 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" Condition="'$(IsProjectNLibrary)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.WinRT.cs" Condition="'$(IsProjectNLibrary)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\Interop.Libraries.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Calendar.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Casing.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Collation.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Idna.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Locale.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Normalization.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Unlink.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Write.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Unix.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Debug.Unix.cs"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Unix.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Unix.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.Unix.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\LocaleData.Unix.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Unix.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.OSX.cs" Condition="'$(TargetsOSX)' == 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Linux.cs" Condition="'$(TargetsOSX)' != 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Unix.cs"/>
private static void EnumCalendarInfoCallback(string calendarString, IntPtr context)
{
- CallbackContext callbackContext = (CallbackContext)((GCHandle)context).Target;
-
- if (callbackContext.DisallowDuplicates)
+ try
{
- foreach (string existingResult in callbackContext.Results)
+ CallbackContext callbackContext = (CallbackContext)((GCHandle)context).Target;
+
+ if (callbackContext.DisallowDuplicates)
{
- if (string.Equals(calendarString, existingResult, StringComparison.Ordinal))
+ foreach (string existingResult in callbackContext.Results)
{
- // the value is already in the results, so don't add it again
- return;
+ if (string.Equals(calendarString, existingResult, StringComparison.Ordinal))
+ {
+ // the value is already in the results, so don't add it again
+ return;
+ }
}
}
- }
- callbackContext.Results.Add(calendarString);
+ callbackContext.Results.Add(calendarString);
+ }
+ catch (Exception e)
+ {
+ Debug.Assert(false, e.ToString());
+ // we ignore the managed exceptions here because EnumCalendarInfoCallback will get called from the native code.
+ // If we don't ignore the exception here that can cause the runtime to fail fast.
+ }
}
private class CallbackContext
}
char[] outputHeap = new char[actualLength];
- fixed (char* pOutputHeap = outputHeap)
+ fixed (char* pOutputHeap = &outputHeap[0])
{
actualLength = Interop.GlobalizationInterop.ToAscii(flags, unicode, count, pOutputHeap, actualLength);
if (actualLength == 0 || actualLength > outputHeap.Length)
else
{
char[] output = new char[count];
- fixed (char* pOutput = output)
+ fixed (char* pOutput = &output[0])
{
return GetUnicodeCore(ascii, count, flags, pOutput, count, reattempt: true);
}
internal int percentDecimalDigits = 2;
[OptionalField(VersionAdded = 2)]
- internal int digitSubstitution = (int) DigitShapes.None;
+ internal int digitSubstitution = (int)DigitShapes.None;
internal bool isReadOnly = false;
Contract.EndContractBlock();
}
- private static void VerifyNativeDigits(string [] nativeDig, string propertyName)
+ private static void VerifyNativeDigits(string[] nativeDig, string propertyName)
{
if (nativeDig == null)
{
{
// Not 1 or 2 UTF-16 code points
throw new ArgumentException(SR.Argument_InvalidNativeDigitValue, propertyName);
- }
+ }
else if (!char.IsSurrogatePair(nativeDig[i][0], nativeDig[i][1]))
{
// 2 UTF-6 code points, but not a surrogate pair
}
}
- private static void VerifyDigitSubstitution(DigitShapes digitSub, string propertyName)
- {
+ private static void VerifyDigitSubstitution(DigitShapes digitSub, string propertyName)
+ {
switch (digitSub)
{
case DigitShapes.Context:
set
{
VerifyWritable();
- VerifyDecimalSeparator(value, "CurrencyDecimalSeparator");
+ VerifyDecimalSeparator(value, nameof(CurrencyDecimalSeparator));
currencyDecimalSeparator = value;
}
}
{
if (value == null)
{
- throw new ArgumentNullException("PercentGroupSizes",
+ throw new ArgumentNullException(nameof(PercentGroupSizes),
SR.ArgumentNull_Obj);
}
Contract.EndContractBlock();
VerifyWritable();
Int32[] inputSizes = (Int32[])value.Clone();
- CheckGroupSize("PercentGroupSizes", inputSizes);
+ CheckGroupSize(nameof(PercentGroupSizes), inputSizes);
percentGroupSizes = inputSizes;
}
}
}
}
- public string [] NativeDigits
+ public string[] NativeDigits
{
- get { return (String[]) nativeDigits.Clone(); }
+ get { return (String[])nativeDigits.Clone(); }
set
{
VerifyWritable();
public DigitShapes DigitSubstitution
{
- get { return (DigitShapes) digitSubstitution; }
+ get { return (DigitShapes)digitSubstitution; }
set
{
VerifyWritable();
VerifyDigitSubstitution(value, nameof(DigitSubstitution));
- digitSubstitution = (int) value;
+ digitSubstitution = (int)value;
}
}
//
////////////////////////////////////////////////////////////////////////////
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+
namespace System.Globalization
{
- using System;
- using System.Runtime.CompilerServices;
- using System.Runtime.Serialization;
- using System.Diagnostics;
- using System.Diagnostics.Contracts;
-
[Serializable]
public partial class SortKey
{
internal string _localeName; // locale identifier
[OptionalField(VersionAdded = 1)] // LCID field so serialization is Whidbey compatible though we don't officially support it
- internal int _win32LCID;
- // Whidbey serialization
+ internal int _win32LCID;
internal CompareOptions _options; // options
internal string _string; // original string
{
_keyData = keyData;
_localeName = localeName;
- _options = options;
- _string = str;
+ _options = options;
+ _string = str;
}
[OnSerializing]
return (_string);
}
}
-
+
////////////////////////////////////////////////////////////////////////
//
// GetKeyData
return (byte[])(_keyData.Clone());
}
}
-
+
////////////////////////////////////////////////////////////////////////
//
// Compare
////////////////////////////////////////////////////////////////////////
public static int Compare(SortKey sortkey1, SortKey sortkey2)
{
- if (sortkey1==null || sortkey2==null)
+ if (sortkey1 == null || sortkey2 == null)
{
throw new ArgumentNullException((sortkey1 == null ? nameof(sortkey1) : nameof(sortkey2)));
}
Contract.EndContractBlock();
+
byte[] key1Data = sortkey1._keyData;
byte[] key2Data = sortkey2._keyData;
-
+
Debug.Assert(key1Data != null, "key1Data != null");
Debug.Assert(key2Data != null, "key2Data != null");
int compLen = (key1Data.Length < key2Data.Length) ? key1Data.Length : key2Data.Length;
- for (int i=0; i<compLen; i++)
+ for (int i = 0; i < compLen; i++)
{
- if (key1Data[i]>key2Data[i])
+ if (key1Data[i] > key2Data[i])
{
return (1);
}
- if (key1Data[i]<key2Data[i])
+ if (key1Data[i] < key2Data[i])
{
return (-1);
}
{
if (null == value)
{
- throw new ArgumentNullException("String",
+ throw new ArgumentNullException(nameof(String),
SR.ArgumentNull_String);
}
Contract.EndContractBlock();
}
}
- public string SubstringByTextElements(int startingTextElement)
+ public string SubstringByTextElements(int startingTextElement)
{
// If the string is empty, no sense going further.
- if (null == this.Indexes)
+ if (null == this.Indexes)
{
// Just decide which error to give depending on the param they gave us....
- if (startingTextElement < 0)
+ if (startingTextElement < 0)
{
throw new ArgumentOutOfRangeException(nameof(startingTextElement), SR.ArgumentOutOfRange_NeedPosNum);
}
return (SubstringByTextElements(startingTextElement, Indexes.Length - startingTextElement));
}
- public string SubstringByTextElements(int startingTextElement, int lengthInTextElements)
+ public string SubstringByTextElements(int startingTextElement, int lengthInTextElements)
{
if (startingTextElement < 0)
{
{
var awaitable = (AsyncCopyToAwaitable)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP);
- Debug.Assert(awaitable._continuation != s_sentinel, "Sentinel must not have already been set as the continuation");
+ Debug.Assert(!ReferenceEquals(awaitable._continuation, s_sentinel), "Sentinel must not have already been set as the continuation");
awaitable._errorCode = errorCode;
awaitable._numBytes = numBytes;
}
public AsyncCopyToAwaitable GetAwaiter() => this;
- public bool IsCompleted => _continuation == s_sentinel;
+ public bool IsCompleted => ReferenceEquals(_continuation, s_sentinel);
public void GetResult() { }
public void OnCompleted(Action continuation) => UnsafeOnCompleted(continuation);
public void UnsafeOnCompleted(Action continuation)
{
- if (_continuation == s_sentinel ||
+ if (ReferenceEquals(_continuation, s_sentinel) ||
Interlocked.CompareExchange(ref _continuation, continuation, null) != null)
{
- Debug.Assert(_continuation == s_sentinel, $"Expected continuation set to s_sentinel, got ${_continuation}");
+ Debug.Assert(ReferenceEquals(_continuation, s_sentinel), $"Expected continuation set to s_sentinel, got ${_continuation}");
Task.Run(continuation);
}
}
// "\\server\share").
public static string GetDirectoryName(string path)
{
- if (path != null)
+ if (string.IsNullOrWhiteSpace(path))
{
- PathInternal.CheckInvalidPathChars(path);
- path = PathInternal.NormalizeDirectorySeparators(path);
- int root = PathInternal.GetRootLength(path);
+ if (path == null) return null;
+ throw new ArgumentException(SR.Arg_PathIllegal, nameof(path));
+ }
- int i = path.Length;
- if (i > root)
- {
- while (i > root && !PathInternal.IsDirectorySeparator(path[--i])) ;
- return path.Substring(0, i);
- }
+ PathInternal.CheckInvalidPathChars(path);
+ path = PathInternal.NormalizeDirectorySeparators(path);
+ int root = PathInternal.GetRootLength(path);
+
+ int i = path.Length;
+ if (i > root)
+ {
+ while (i > root && !PathInternal.IsDirectorySeparator(path[--i])) ;
+ return path.Substring(0, i);
}
+
return null;
}
private byte[] _array;
private GCHandle _pinningHandle;
- // The new inheritance model requires a Critical default ctor since base (UnmanagedMemoryStream) has one
- private PinnedBufferMemoryStream() : base() { }
-
internal PinnedBufferMemoryStream(byte[] array)
{
Debug.Assert(array != null, "Array can't be null");
}
_array = array;
- _pinningHandle = new GCHandle(array, GCHandleType.Pinned);
+ _pinningHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
// Now the byte[] is pinned for the lifetime of this instance.
// But I also need to get a pointer to that block of memory...
fixed (byte* ptr = &_array[0])
protected override void Dispose(bool disposing)
{
- if (_isOpen)
+ if (_pinningHandle.IsAllocated)
{
_pinningHandle.Free();
- _isOpen = false;
- }
-#if _DEBUG
- // To help track down lifetime issues on checked builds, force
- //a full GC here.
- if (disposing)
- {
- GC.Collect();
- GC.WaitForPendingFinalizers();
}
-#endif
+
base.Dispose(disposing);
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-/*============================================================
-**
-**
-**
-**
-** Purpose: Create a stream over unmanaged memory, mostly
-** useful for memory-mapped files.
-**
-**
-===========================================================*/
-
-using System;
-using System.Runtime;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Security;
using System.Threading;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
using System.Threading.Tasks;
-
namespace System.IO
{
/*
* call free, run a user-provided delegate to free the memory, etc.
* We'll suggest user write a subclass of UnmanagedMemoryStream that uses
* a SafeHandle subclass to hold onto the memory.
- * Check for problems when using this in the negative parts of a
- * process's address space. We may need to use unsigned longs internally
- * and change the overflow detection logic.
- *
- * -----SECURITY MODEL AND SILVERLIGHT-----
- * A few key notes about exposing UMS in silverlight:
- * 1. No ctors are exposed to transparent code. This version of UMS only
- * supports byte* (not SafeBuffer). Therefore, framework code can create
- * a UMS and hand it to transparent code. Transparent code can use most
- * operations on a UMS, but not operations that directly expose a
- * pointer.
- *
- * 2. Scope of "unsafe" and non-CLS compliant operations reduced: The
- * Whidbey version of this class has CLSCompliant(false) at the class
- * level and unsafe modifiers at the method level. These were reduced to
- * only where the unsafe operation is performed -- i.e. immediately
- * around the pointer manipulation. Note that this brings UMS in line
- * with recent changes in pu/clr to support SafeBuffer.
*
- * 3. Currently, the only caller that creates a UMS is ResourceManager,
- * which creates read-only UMSs, and therefore operations that can
- * change the length will throw because write isn't supported. A
- * conservative option would be to formalize the concept that _only_
- * read-only UMSs can be creates, and enforce this by making WriteX and
- * SetLength SecurityCritical. However, this is a violation of
- * security inheritance rules, so we must keep these safe. The
- * following notes make this acceptable for future use.
- * a. a race condition in WriteX that could have allowed a thread to
- * read from unzeroed memory was fixed
- * b. memory region cannot be expanded beyond _capacity; in other
- * words, a UMS creator is saying a writable UMS is safe to
- * write to anywhere in the memory range up to _capacity, specified
- * in the ctor. Even if the caller doesn't specify a capacity, then
- * length is used as the capacity.
*/
+
+ /// <summary>
+ /// Stream over a memory pointer or over a SafeBuffer
+ /// </summary>
public class UnmanagedMemoryStream : Stream
{
- private const long UnmanagedMemStreamMaxLength = Int64.MaxValue;
-
private SafeBuffer _buffer;
private unsafe byte* _mem;
private long _length;
private long _position;
private long _offset;
private FileAccess _access;
- internal bool _isOpen;
- [NonSerialized]
+ private bool _isOpen;
private Task<Int32> _lastReadTask; // The last successful task returned from ReadAsync
-
+ /// <summary>
+ /// Creates a closed stream.
+ /// </summary>
// Needed for subclasses that need to map a file, etc.
protected UnmanagedMemoryStream()
{
_isOpen = false;
}
+ /// <summary>
+ /// Creates a stream over a SafeBuffer.
+ /// </summary>
+ /// <param name="buffer"></param>
+ /// <param name="offset"></param>
+ /// <param name="length"></param>
public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length)
{
Initialize(buffer, offset, length, FileAccess.Read);
}
+ /// <summary>
+ /// Creates a stream over a SafeBuffer.
+ /// </summary>
public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access)
{
Initialize(buffer, offset, length, access);
}
+ /// <summary>
+ /// Subclasses must call this method (or the other overload) to properly initialize all instance fields.
+ /// </summary>
+ /// <param name="buffer"></param>
+ /// <param name="offset"></param>
+ /// <param name="length"></param>
+ /// <param name="access"></param>
protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access)
{
if (buffer == null)
_isOpen = true;
}
+ /// <summary>
+ /// Creates a stream over a byte*.
+ /// </summary>
[CLSCompliant(false)]
public unsafe UnmanagedMemoryStream(byte* pointer, long length)
{
Initialize(pointer, length, length, FileAccess.Read);
}
+ /// <summary>
+ /// Creates a stream over a byte*.
+ /// </summary>
[CLSCompliant(false)]
public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access)
{
Initialize(pointer, length, capacity, access);
}
+ /// <summary>
+ /// Subclasses must call this method (or the other overload) to properly initialize all instance fields.
+ /// </summary>
[CLSCompliant(false)]
protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access)
{
_isOpen = true;
}
+ /// <summary>
+ /// Returns true if the stream can be read; otherwise returns false.
+ /// </summary>
public override bool CanRead
{
[Pure]
get { return _isOpen && (_access & FileAccess.Read) != 0; }
}
+ /// <summary>
+ /// Returns true if the stream can seek; otherwise returns false.
+ /// </summary>
public override bool CanSeek
{
[Pure]
get { return _isOpen; }
}
+ /// <summary>
+ /// Returns true if the stream can be written to; otherwise returns false.
+ /// </summary>
public override bool CanWrite
{
[Pure]
get { return _isOpen && (_access & FileAccess.Write) != 0; }
}
+ /// <summary>
+ /// Closes the stream. The stream's memory needs to be dealt with separately.
+ /// </summary>
+ /// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
_isOpen = false;
base.Dispose(disposing);
}
+ /// <summary>
+ /// Since it's a memory stream, this method does nothing.
+ /// </summary>
public override void Flush()
{
- if (!_isOpen) __Error.StreamIsClosed();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
}
+ /// <summary>
+ /// Since it's a memory stream, this method does nothing specific.
+ /// </summary>
+ /// <param name="cancellationToken"></param>
+ /// <returns></returns>
public override Task FlushAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
}
}
-
+ /// <summary>
+ /// Number of bytes in the stream.
+ /// </summary>
public override long Length
{
get
{
- if (!_isOpen) __Error.StreamIsClosed();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
return Interlocked.Read(ref _length);
}
}
+ /// <summary>
+ /// Number of bytes that can be written to the stream.
+ /// </summary>
public long Capacity
{
get
{
- if (!_isOpen) __Error.StreamIsClosed();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
return _capacity;
}
}
+ /// <summary>
+ /// ReadByte will read byte at the Position in the stream
+ /// </summary>
public override long Position
{
get
{
- if (!CanSeek) __Error.StreamIsClosed();
+ if (!CanSeek) throw Error.GetStreamIsClosed();
Contract.EndContractBlock();
return Interlocked.Read(ref _position);
}
set
{
- if (value < 0)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (!CanSeek) throw Error.GetStreamIsClosed();
Contract.EndContractBlock();
- if (!CanSeek) __Error.StreamIsClosed();
-#if !BIT64
- unsafe {
- // On 32 bit machines, ensure we don't wrap around.
- if (value > (long) Int32.MaxValue || _mem + value < _mem)
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength);
- }
-#endif
Interlocked.Exchange(ref _position, value);
}
}
+ /// <summary>
+ /// Pointer to memory at the current Position in the stream.
+ /// </summary>
[CLSCompliant(false)]
public unsafe byte* PositionPointer
{
get
{
- if (_buffer != null)
- {
- throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
- }
+ if (_buffer != null) throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
+ if (!_isOpen) throw Error.GetStreamIsClosed();
// Use a temp to avoid a race
long pos = Interlocked.Read(ref _position);
if (pos > _capacity)
throw new IndexOutOfRangeException(SR.IndexOutOfRange_UMSPosition);
byte* ptr = _mem + pos;
- if (!_isOpen) __Error.StreamIsClosed();
return ptr;
}
set
{
- if (_buffer != null)
- throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
- if (!_isOpen) __Error.StreamIsClosed();
+ if (_buffer != null) throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
+ if (!_isOpen) throw Error.GetStreamIsClosed();
- // Note: subtracting pointers returns an Int64. Working around
- // to avoid hitting compiler warning CS0652 on this line.
- if (new IntPtr(value - _mem).ToInt64() > UnmanagedMemStreamMaxLength)
- throw new ArgumentOutOfRangeException("offset", SR.ArgumentOutOfRange_UnmanagedMemStreamLength);
if (value < _mem)
throw new IOException(SR.IO_SeekBeforeBegin);
+ long newPosition = (long)value - (long)_mem;
+ if (newPosition < 0)
+ throw new ArgumentOutOfRangeException("offset", SR.ArgumentOutOfRange_UnmanagedMemStreamLength);
- Interlocked.Exchange(ref _position, value - _mem);
- }
- }
-
- internal unsafe byte* Pointer
- {
- get
- {
- if (_buffer != null)
- throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
-
- return _mem;
+ Interlocked.Exchange(ref _position, newPosition);
}
}
- public override int Read([In, Out] byte[] buffer, int offset, int count)
+ /// <summary>
+ /// Reads bytes from stream and puts them into the buffer
+ /// </summary>
+ /// <param name="buffer">Buffer to read the bytes to.</param>
+ /// <param name="offset">Starting index in the buffer.</param>
+ /// <param name="count">Maximum number of bytes to read.</param>
+ /// <returns>Number of bytes actually read.</returns>
+ public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
throw new ArgumentException(SR.Argument_InvalidOffLen);
Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync
- if (!_isOpen) __Error.StreamIsClosed();
- if (!CanRead) __Error.ReadNotSupported();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
+ if (!CanRead) throw Error.GetReadNotSupported();
// Use a local variable to avoid a race where another thread
// changes our position after we decide we can read some bytes.
return nInt;
}
+ /// <summary>
+ /// Reads bytes from stream and puts them into the buffer
+ /// </summary>
+ /// <param name="buffer">Buffer to read the bytes to.</param>
+ /// <param name="offset">Starting index in the buffer.</param>
+ /// <param name="count">Maximum number of bytes to read.</param>
+ /// <param name="cancellationToken">Token that can be used to cancel this operation.</param>
+ /// <returns>Task that can be used to access the number of bytes actually read.</returns>
public override Task<Int32> ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
{
if (buffer == null)
}
}
+ /// <summary>
+ /// Returns the byte at the stream current Position and advances the Position.
+ /// </summary>
+ /// <returns></returns>
public override int ReadByte()
{
- if (!_isOpen) __Error.StreamIsClosed();
- if (!CanRead) __Error.ReadNotSupported();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
+ if (!CanRead) throw Error.GetReadNotSupported();
long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
long len = Interlocked.Read(ref _length);
return result;
}
+ /// <summary>
+ /// Advanced the Position to specific location in the stream.
+ /// </summary>
+ /// <param name="offset">Offset from the loc parameter.</param>
+ /// <param name="loc">Origin for the offset parameter.</param>
+ /// <returns></returns>
public override long Seek(long offset, SeekOrigin loc)
{
- if (!_isOpen) __Error.StreamIsClosed();
- if (offset > UnmanagedMemStreamMaxLength)
- throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_UnmanagedMemStreamLength);
+ if (!_isOpen) throw Error.GetStreamIsClosed();
switch (loc)
{
case SeekOrigin.Begin:
return finalPos;
}
+ /// <summary>
+ /// Sets the Length of the stream.
+ /// </summary>
+ /// <param name="value"></param>
public override void SetLength(long value)
{
if (value < 0)
- throw new ArgumentOutOfRangeException("length", SR.ArgumentOutOfRange_NeedNonNegNum);
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
Contract.EndContractBlock();
if (_buffer != null)
throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer);
- if (!_isOpen) __Error.StreamIsClosed();
- if (!CanWrite) __Error.WriteNotSupported();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
+ if (!CanWrite) throw Error.GetWriteNotSupported();
if (value > _capacity)
throw new IOException(SR.IO_FixedCapacity);
}
}
+ /// <summary>
+ /// Writes buffer into the stream
+ /// </summary>
+ /// <param name="buffer">Buffer that will be written.</param>
+ /// <param name="offset">Starting index in the buffer.</param>
+ /// <param name="count">Number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentException(SR.Argument_InvalidOffLen);
Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..)
- if (!_isOpen) __Error.StreamIsClosed();
- if (!CanWrite) __Error.WriteNotSupported();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
+ if (!CanWrite) throw Error.GetWriteNotSupported();
long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
long len = Interlocked.Read(ref _length);
return;
}
+ /// <summary>
+ /// Writes buffer into the stream. The operation completes synchronously.
+ /// </summary>
+ /// <param name="buffer">Buffer that will be written.</param>
+ /// <param name="offset">Starting index in the buffer.</param>
+ /// <param name="count">Number of bytes to write.</param>
+ /// <param name="cancellationToken">Token that can be used to cancel the operation.</param>
+ /// <returns>Task that can be awaited </returns>
public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
{
if (buffer == null)
catch (Exception ex)
{
Debug.Assert(!(ex is OperationCanceledException));
- return Task.FromException<Int32>(ex);
+ return Task.FromException(ex);
}
}
-
+ /// <summary>
+ /// Writes a byte to the stream and advances the current Position.
+ /// </summary>
+ /// <param name="value"></param>
public override void WriteByte(byte value)
{
- if (!_isOpen) __Error.StreamIsClosed();
- if (!CanWrite) __Error.WriteNotSupported();
+ if (!_isOpen) throw Error.GetStreamIsClosed();
+ if (!CanWrite) throw Error.GetWriteNotSupported();
long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
long len = Interlocked.Read(ref _length);
public override bool CanRead
{
- [Pure]
get { return _unmanagedStream.CanRead; }
}
public override bool CanSeek
{
- [Pure]
get { return _unmanagedStream.CanSeek; }
}
public override bool CanWrite
{
- [Pure]
get { return _unmanagedStream.CanWrite; }
}
try
{
if (disposing)
- _unmanagedStream.Close();
+ _unmanagedStream.Dispose();
}
finally
{
}
}
- public override int Read([In, Out] byte[] buffer, int offset, int count)
+ public override int Read(byte[] buffer, int offset, int count)
{
return _unmanagedStream.Read(buffer, offset, count);
}
public unsafe override byte[] ToArray()
{
- if (!_unmanagedStream._isOpen) __Error.StreamIsClosed();
- if (!_unmanagedStream.CanRead) __Error.ReadNotSupported();
-
byte[] buffer = new byte[_unmanagedStream.Length];
- Buffer.Memcpy(buffer, 0, _unmanagedStream.Pointer, 0, (int)_unmanagedStream.Length);
+ _unmanagedStream.Read(buffer, 0, (int)_unmanagedStream.Length);
return buffer;
}
throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream);
Contract.EndContractBlock();
- if (!_unmanagedStream._isOpen) __Error.StreamIsClosed();
- if (!CanRead) __Error.ReadNotSupported();
-
byte[] buffer = ToArray();
stream.Write(buffer, 0, buffer.Length);
} // class UnmanagedMemoryStreamWrapper
} // namespace
-
// into smaller chunks, each of size sqrt(n), would be substantially better for
// resource files containing thousands of resources.
//
- internal sealed class RuntimeResourceSet : ResourceSet, IEnumerable
+#if CORECLR
+ internal
+#else
+ public // On CoreRT, this must be public because of need to whitelist past the ReflectionBlock.
+#endif
+ sealed class RuntimeResourceSet : ResourceSet, IEnumerable
{
internal const int Version = 2; // File format version number
using System.Security;
using System.Text;
using System.Configuration.Assemblies;
- using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32.SafeHandles;
{
using System;
using System.Reflection;
- using System.Runtime.Remoting;
using System.Security;
using CultureInfo = System.Globalization.CultureInfo;
using Evidence = System.Security.Policy.Evidence;
return Activator.CreateInstance(type, false);
}
- /*
- * Create an instance using the name of type and the assembly where it exists. This allows
- * types to be created remotely without having to load the type locally.
- */
-
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
- static public ObjectHandle CreateInstance(String assemblyName,
- String typeName)
- {
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return CreateInstance(assemblyName,
- typeName,
- false,
- Activator.ConstructorDefault,
- null,
- null,
- null,
- null,
- null,
- ref stackMark);
- }
-
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
- static public ObjectHandle CreateInstance(String assemblyName,
- String typeName,
- Object[] activationAttributes)
-
- {
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return CreateInstance(assemblyName,
- typeName,
- false,
- Activator.ConstructorDefault,
- null,
- null,
- null,
- activationAttributes,
- null,
- ref stackMark);
- }
-
[System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
static public Object CreateInstance(Type type, bool nonPublic)
{
// Skip the CreateInstanceCheckThis call to avoid perf cost and to maintain compatibility with V2 (throwing the same exceptions).
return (T)rt.CreateInstanceDefaultCtor(true /*publicOnly*/, true /*skipCheckThis*/, true /*fillCache*/, ref stackMark);
}
-
- static public ObjectHandle CreateInstanceFrom(String assemblyFile,
- String typeName)
-
- {
- return CreateInstanceFrom(assemblyFile, typeName, null);
- }
-
- static public ObjectHandle CreateInstanceFrom(String assemblyFile,
- String typeName,
- Object[] activationAttributes)
-
- {
- return CreateInstanceFrom(assemblyFile,
- typeName,
- false,
- Activator.ConstructorDefault,
- null,
- null,
- null,
- activationAttributes);
- }
-
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
- public static ObjectHandle CreateInstance(string assemblyName,
- string typeName,
- bool ignoreCase,
- BindingFlags bindingAttr,
- Binder binder,
- object[] args,
- CultureInfo culture,
- object[] activationAttributes)
- {
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return CreateInstance(assemblyName,
- typeName,
- ignoreCase,
- bindingAttr,
- binder,
- args,
- culture,
- activationAttributes,
- null,
- ref stackMark);
- }
-
- static internal ObjectHandle CreateInstance(String assemblyString,
- String typeName,
- bool ignoreCase,
- BindingFlags bindingAttr,
- Binder binder,
- Object[] args,
- CultureInfo culture,
- Object[] activationAttributes,
- Evidence securityInfo,
- ref StackCrawlMark stackMark)
- {
- Type type = null;
- Assembly assembly = null;
- if (assemblyString == null)
- {
- assembly = RuntimeAssembly.GetExecutingAssembly(ref stackMark);
- }
- else
- {
- RuntimeAssembly assemblyFromResolveEvent;
- AssemblyName assemblyName = RuntimeAssembly.CreateAssemblyName(assemblyString, false /*forIntrospection*/, out assemblyFromResolveEvent);
- if (assemblyFromResolveEvent != null)
- {
- // Assembly was resolved via AssemblyResolve event
- assembly = assemblyFromResolveEvent;
- }
- else if (assemblyName.ContentType == AssemblyContentType.WindowsRuntime)
- {
- // WinRT type - we have to use Type.GetType
- type = Type.GetType(typeName + ", " + assemblyString, true /*throwOnError*/, ignoreCase);
- }
- else
- {
- // Classic managed type
- assembly = RuntimeAssembly.InternalLoadAssemblyName(
- assemblyName, securityInfo, null, ref stackMark,
- true /*thrownOnFileNotFound*/, false /*forIntrospection*/);
- }
- }
-
- if (type == null)
- {
- // It's classic managed type (not WinRT type)
- Log(assembly != null, "CreateInstance:: ", "Loaded " + assembly.FullName, "Failed to Load: " + assemblyString);
- if (assembly == null) return null;
-
- type = assembly.GetType(typeName, true /*throwOnError*/, ignoreCase);
- }
-
- Object o = Activator.CreateInstance(type,
- bindingAttr,
- binder,
- args,
- culture,
- activationAttributes);
-
- Log(o != null, "CreateInstance:: ", "Created Instance of class " + typeName, "Failed to create instance of class " + typeName);
- if (o == null)
- return null;
- else
- {
- ObjectHandle Handle = new ObjectHandle(o);
- return Handle;
- }
- }
-
- public static ObjectHandle CreateInstanceFrom(string assemblyFile,
- string typeName,
- bool ignoreCase,
- BindingFlags bindingAttr,
- Binder binder,
- object[] args,
- CultureInfo culture,
- object[] activationAttributes)
- {
- return CreateInstanceFromInternal(assemblyFile,
- typeName,
- ignoreCase,
- bindingAttr,
- binder,
- args,
- culture,
- activationAttributes,
- null);
- }
-
- private static ObjectHandle CreateInstanceFromInternal(String assemblyFile,
- String typeName,
- bool ignoreCase,
- BindingFlags bindingAttr,
- Binder binder,
- Object[] args,
- CultureInfo culture,
- Object[] activationAttributes,
- Evidence securityInfo)
- {
-#pragma warning disable 618
- Assembly assembly = Assembly.LoadFrom(assemblyFile, securityInfo);
-#pragma warning restore 618
- Type t = assembly.GetType(typeName, true, ignoreCase);
-
- Object o = Activator.CreateInstance(t,
- bindingAttr,
- binder,
- args,
- culture,
- activationAttributes);
-
- Log(o != null, "CreateInstanceFrom:: ", "Created Instance of class " + typeName, "Failed to create instance of class " + typeName);
- if (o == null)
- return null;
- else
- {
- ObjectHandle Handle = new ObjectHandle(o);
- return Handle;
- }
- }
-
- public static ObjectHandle CreateComInstanceFrom(String assemblyName,
- String typeName)
- {
- return CreateComInstanceFrom(assemblyName,
- typeName,
- null,
- AssemblyHashAlgorithm.None);
- }
-
- public static ObjectHandle CreateComInstanceFrom(String assemblyName,
- String typeName,
- byte[] hashValue,
- AssemblyHashAlgorithm hashAlgorithm)
- {
- Assembly assembly = Assembly.LoadFrom(assemblyName, hashValue, hashAlgorithm);
-
- Type t = assembly.GetType(typeName, true, false);
-
- Object[] Attr = t.GetCustomAttributes(typeof(ComVisibleAttribute), false);
- if (Attr.Length > 0)
- {
- if (((ComVisibleAttribute)Attr[0]).Value == false)
- throw new TypeLoadException(SR.Argument_TypeMustBeVisibleFromCom);
- }
-
- Log(assembly != null, "CreateInstance:: ", "Loaded " + assembly.FullName, "Failed to Load: " + assemblyName);
-
- if (assembly == null) return null;
-
-
- Object o = Activator.CreateInstance(t,
- Activator.ConstructorDefault,
- null,
- null,
- null,
- null);
-
- Log(o != null, "CreateInstance:: ", "Created Instance of class " + typeName, "Failed to create instance of class " + typeName);
- if (o == null)
- return null;
- else
- {
- ObjectHandle Handle = new ObjectHandle(o);
- return Handle;
- }
- }
-
- [System.Diagnostics.Conditional("_DEBUG")]
- private static void Log(bool test, string title, string success, string failure)
- {
- }
}
}
-
using System.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;
- using System.Runtime.Remoting;
using System.Reflection.Emit;
using CultureInfo = System.Globalization.CultureInfo;
using System.IO;
private static extern APPX_FLAGS nGetAppXFlags();
#endif
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern void GetAppDomainManagerType(AppDomainHandle domain,
- StringHandleOnStack retAssembly,
- StringHandleOnStack retType);
-
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern void SetAppDomainManagerType(AppDomainHandle domain,
- string assembly,
- string type);
-
[SuppressUnmanagedCodeSecurity]
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void SetSecurityHomogeneousFlag(AppDomainHandle domain,
SetupBindingPaths(trustedPlatformAssemblies, platformResourceRoots, appPaths, appNiPaths, appLocalWinMD);
}
- string domainManagerAssembly;
- string domainManagerType;
- GetAppDomainManagerType(out domainManagerAssembly, out domainManagerType);
-
- if (domainManagerAssembly != null && domainManagerType != null)
- {
- try
- {
- _domainManager = CreateInstanceAndUnwrap(domainManagerAssembly, domainManagerType) as AppDomainManager;
- }
- catch (FileNotFoundException e)
- {
- throw new TypeLoadException(SR.Argument_NoDomainManager, e);
- }
- catch (SecurityException e)
- {
- throw new TypeLoadException(SR.Argument_NoDomainManager, e);
- }
- catch (TypeLoadException e)
- {
- throw new TypeLoadException(SR.Argument_NoDomainManager, e);
- }
-
- if (_domainManager == null)
- {
- throw new TypeLoadException(SR.Argument_NoDomainManager);
- }
-
- // If this domain was not created by a managed call to CreateDomain, then the AppDomainSetup
- // will not have the correct values for the AppDomainManager set.
- FusionStore.AppDomainManagerAssembly = domainManagerAssembly;
- FusionStore.AppDomainManagerType = domainManagerType;
-
- bool notifyFusion = _domainManager.GetType() != typeof(System.AppDomainManager) && !DisableFusionUpdatesFromADManager();
-
-
-
- AppDomainSetup FusionStoreOld = null;
- if (notifyFusion)
- FusionStoreOld = new AppDomainSetup(FusionStore, true);
-
- // Initialize the AppDomainMAnager and register the instance with the native host if requested
- _domainManager.InitializeNewDomain(FusionStore);
-
- if (notifyFusion)
- SetupFusionStore(_FusionStore, FusionStoreOld); // Notify Fusion about the changes the user implementation of InitializeNewDomain may have made to the FusionStore object.
- }
-
InitializeCompatibilityFlags();
}
}
/// <summary>
- /// Get the name of the assembly and type that act as the AppDomainManager for this domain
- /// </summary>
- internal void GetAppDomainManagerType(out string assembly, out string type)
- {
- // We can't just use our parameters because we need to ensure that the strings used for hte QCall
- // are on the stack.
- string localAssembly = null;
- string localType = null;
-
- GetAppDomainManagerType(GetNativeHandle(),
- JitHelpers.GetStringHandleOnStack(ref localAssembly),
- JitHelpers.GetStringHandleOnStack(ref localType));
-
- assembly = localAssembly;
- type = localType;
- }
-
- /// <summary>
- /// Set the assembly and type which act as the AppDomainManager for this domain
- /// </summary>
- private void SetAppDomainManagerType(string assembly, string type)
- {
- Debug.Assert(assembly != null, "assembly != null");
- Debug.Assert(type != null, "type != null");
- SetAppDomainManagerType(GetNativeHandle(), assembly, type);
- }
-
- /// <summary>
/// Called for every AppDomain (including the default domain) to initialize the security of the AppDomain)
/// </summary>
private void InitializeDomainSecurity(Evidence providedSecurityInfo,
}
}
-
- public ObjectHandle CreateInstance(String assemblyName,
- String typeName)
-
- {
- // jit does not check for that, so we should do it ...
- if (this == null)
- throw new NullReferenceException();
-
- if (assemblyName == null)
- throw new ArgumentNullException(nameof(assemblyName));
- Contract.EndContractBlock();
-
- return Activator.CreateInstance(assemblyName,
- typeName);
- }
-
public static AppDomain CurrentDomain
{
get
}
#endif // FEATURE_COMINTEROP
- // set up the AppDomainManager for this domain and initialize security.
- if (adSetup.AppDomainManagerAssembly != null && adSetup.AppDomainManagerType != null)
- {
- ad.SetAppDomainManagerType(adSetup.AppDomainManagerAssembly, adSetup.AppDomainManagerType);
- }
-
ad.CreateAppDomainManager(); // could modify FusionStore's object
ad.InitializeDomainSecurity(providedSecurityInfo,
creatorsSecurityInfo,
}
}
- public Object CreateInstanceAndUnwrap(String assemblyName,
- String typeName)
- {
- ObjectHandle oh = CreateInstance(assemblyName, typeName);
- if (oh == null)
- return null;
-
- return oh.Unwrap();
- } // CreateInstanceAndUnwrap
-
public Int32 Id
{
get
{
using System.IO;
using System.Text;
- using System.Runtime.Remoting;
using System.Diagnostics;
using Microsoft.Win32;
using System.Runtime.CompilerServices;
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-**
-**
-** Purpose: Implements a dynamically sized List as an array,
-** and provides many convenience methods for treating
-** an array as an IList.
-**
-**
-===========================================================*/
-
-using System;
-using System.Runtime;
-using System.Security;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.Serialization;
-using System.Diagnostics.CodeAnalysis;
-using System.Diagnostics.Contracts;
-
-namespace System.Collections
-{
- // Implements a variable-size List that uses an array of objects to store the
- // elements. A ArrayList has a capacity, which is the allocated length
- // of the internal array. As elements are added to a ArrayList, the capacity
- // of the ArrayList is automatically increased as required by reallocating the
- // internal array.
- //
- [FriendAccessAllowed]
- [DebuggerTypeProxy(typeof(System.Collections.ArrayList.ArrayListDebugView))]
- [DebuggerDisplay("Count = {Count}")]
- [Serializable]
- internal class ArrayList : IList, ICloneable
- {
- private Object[] _items;
- [ContractPublicPropertyName("Count")]
- private int _size;
- private int _version;
- [NonSerialized]
- private Object _syncRoot;
-
- private const int _defaultCapacity = 4;
- private static readonly Object[] emptyArray = Array.Empty<Object>();
-
- // Constructs a ArrayList. The list is initially empty and has a capacity
- // of zero. Upon adding the first element to the list the capacity is
- // increased to _defaultCapacity, and then increased in multiples of two as required.
- public ArrayList()
- {
- _items = emptyArray;
- }
-
- // Constructs a ArrayList with a given initial capacity. The list is
- // initially empty, but will have room for the given number of elements
- // before any reallocations are required.
- //
- public ArrayList(int capacity)
- {
- if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(capacity)));
- Contract.EndContractBlock();
-
- if (capacity == 0)
- _items = emptyArray;
- else
- _items = new Object[capacity];
- }
-
- // Constructs a ArrayList, copying the contents of the given collection. The
- // size and capacity of the new list will both be equal to the size of the
- // given collection.
- //
- public ArrayList(ICollection c)
- {
- if (c == null)
- throw new ArgumentNullException(nameof(c), SR.ArgumentNull_Collection);
- Contract.EndContractBlock();
-
- int count = c.Count;
- if (count == 0)
- {
- _items = emptyArray;
- }
- else
- {
- _items = new Object[count];
- AddRange(c);
- }
- }
-
- // Gets and sets the capacity of this list. The capacity is the size of
- // the internal array used to hold items. When set, the internal
- // array of the list is reallocated to the given capacity.
- //
- public virtual int Capacity
- {
- get
- {
- Contract.Ensures(Contract.Result<int>() >= Count);
- return _items.Length;
- }
- set
- {
- if (value < _size)
- {
- throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
- }
- Contract.Ensures(Capacity >= 0);
- Contract.EndContractBlock();
- // We don't want to update the version number when we change the capacity.
- // Some existing applications have dependency on this.
- if (value != _items.Length)
- {
- if (value > 0)
- {
- Object[] newItems = new Object[value];
- if (_size > 0)
- {
- Array.Copy(_items, 0, newItems, 0, _size);
- }
- _items = newItems;
- }
- else
- {
- _items = new Object[_defaultCapacity];
- }
- }
- }
- }
-
- // Read-only property describing how many elements are in the List.
- public virtual int Count
- {
- get
- {
- Contract.Ensures(Contract.Result<int>() >= 0);
- return _size;
- }
- }
-
- public virtual bool IsFixedSize
- {
- get { return false; }
- }
-
-
- // Is this ArrayList read-only?
- public virtual bool IsReadOnly
- {
- get { return false; }
- }
-
- // Is this ArrayList synchronized (thread-safe)?
- public virtual bool IsSynchronized
- {
- get { return false; }
- }
-
- // Synchronization root for this object.
- public virtual Object SyncRoot
- {
- get
- {
- if (_syncRoot == null)
- {
- System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
- }
- return _syncRoot;
- }
- }
-
- // Sets or Gets the element at the given index.
- //
- public virtual Object this[int index]
- {
- get
- {
- if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
- Contract.EndContractBlock();
- return _items[index];
- }
- set
- {
- if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
- Contract.EndContractBlock();
- _items[index] = value;
- _version++;
- }
- }
-
- // Adds the given object to the end of this list. The size of the list is
- // increased by one. If required, the capacity of the list is doubled
- // before adding the new element.
- //
- public virtual int Add(Object value)
- {
- Contract.Ensures(Contract.Result<int>() >= 0);
- if (_size == _items.Length) EnsureCapacity(_size + 1);
- _items[_size] = value;
- _version++;
- return _size++;
- }
-
- // Adds the elements of the given collection to the end of this list. If
- // required, the capacity of the list is increased to twice the previous
- // capacity or the new size, whichever is larger.
- //
- public virtual void AddRange(ICollection c)
- {
- InsertRange(_size, c);
- }
-
-
- // Clears the contents of ArrayList.
- public virtual void Clear()
- {
- if (_size > 0)
- {
- Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
- _size = 0;
- }
- _version++;
- }
-
- // Clones this ArrayList, doing a shallow copy. (A copy is made of all
- // Object references in the ArrayList, but the Objects pointed to
- // are not cloned).
- public virtual Object Clone()
- {
- Contract.Ensures(Contract.Result<Object>() != null);
- ArrayList la = new ArrayList(_size);
- la._size = _size;
- la._version = _version;
- Array.Copy(_items, 0, la._items, 0, _size);
- return la;
- }
-
-
- // Contains returns true if the specified element is in the ArrayList.
- // It does a linear, O(n) search. Equality is determined by calling
- // item.Equals().
- //
- public virtual bool Contains(Object item)
- {
- if (item == null)
- {
- for (int i = 0; i < _size; i++)
- if (_items[i] == null)
- return true;
- return false;
- }
- else
- {
- for (int i = 0; i < _size; i++)
- if ((_items[i] != null) && (_items[i].Equals(item)))
- return true;
- return false;
- }
- }
-
- // Copies this ArrayList into array, which must be of a
- // compatible array type.
- //
- public virtual void CopyTo(Array array, int arrayIndex)
- {
- if ((array != null) && (array.Rank != 1))
- throw new ArgumentException(SR.Arg_RankMultiDimNotSupported);
- Contract.EndContractBlock();
- // Delegate rest of error checking to Array.Copy.
- Array.Copy(_items, 0, array, arrayIndex, _size);
- }
-
- // Ensures that the capacity of this list is at least the given minimum
- // value. If the currect capacity of the list is less than min, the
- // capacity is increased to twice the current capacity or to min,
- // whichever is larger.
- private void EnsureCapacity(int min)
- {
- if (_items.Length < min)
- {
- int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2;
- // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
- // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
- if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
- if (newCapacity < min) newCapacity = min;
- Capacity = newCapacity;
- }
- }
-
- // Returns an enumerator for this list with the given
- // permission for removal of elements. If modifications made to the list
- // while an enumeration is in progress, the MoveNext and
- // GetObject methods of the enumerator will throw an exception.
- //
- public virtual IEnumerator GetEnumerator()
- {
- Contract.Ensures(Contract.Result<IEnumerator>() != null);
- return new ArrayListEnumeratorSimple(this);
- }
-
- // Returns the index of the first occurrence of a given value in a range of
- // this list. The list is searched forwards from beginning to end.
- // The elements of the list are compared to the given value using the
- // Object.Equals method.
- //
- // This method uses the Array.IndexOf method to perform the
- // search.
- //
- public virtual int IndexOf(Object value)
- {
- Contract.Ensures(Contract.Result<int>() < Count);
- return Array.IndexOf((Array)_items, value, 0, _size);
- }
-
- // Inserts an element into this list at a given index. The size of the list
- // is increased by one. If required, the capacity of the list is doubled
- // before inserting the new element.
- //
- public virtual void Insert(int index, Object value)
- {
- // Note that insertions at the end are legal.
- if (index < 0 || index > _size) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_ArrayListInsert);
- //Contract.Ensures(Count == Contract.OldValue(Count) + 1);
- Contract.EndContractBlock();
-
- if (_size == _items.Length) EnsureCapacity(_size + 1);
- if (index < _size)
- {
- Array.Copy(_items, index, _items, index + 1, _size - index);
- }
- _items[index] = value;
- _size++;
- _version++;
- }
-
- // Inserts the elements of the given collection at a given index. If
- // required, the capacity of the list is increased to twice the previous
- // capacity or the new size, whichever is larger. Ranges may be added
- // to the end of the list by setting index to the ArrayList's size.
- //
- public virtual void InsertRange(int index, ICollection c)
- {
- if (c == null)
- throw new ArgumentNullException(nameof(c), SR.ArgumentNull_Collection);
- if (index < 0 || index > _size) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
- //Contract.Ensures(Count == Contract.OldValue(Count) + c.Count);
- Contract.EndContractBlock();
-
- int count = c.Count;
- if (count > 0)
- {
- EnsureCapacity(_size + count);
- // shift existing items
- if (index < _size)
- {
- Array.Copy(_items, index, _items, index + count, _size - index);
- }
-
- Object[] itemsToInsert = new Object[count];
- c.CopyTo(itemsToInsert, 0);
- itemsToInsert.CopyTo(_items, index);
- _size += count;
- _version++;
- }
- }
-
- // Returns a read-only IList wrapper for the given IList.
- //
- [FriendAccessAllowed]
- public static IList ReadOnly(IList list)
- {
- if (list == null)
- throw new ArgumentNullException(nameof(list));
- Contract.Ensures(Contract.Result<IList>() != null);
- Contract.EndContractBlock();
- return new ReadOnlyList(list);
- }
-
- // Removes the element at the given index. The size of the list is
- // decreased by one.
- //
- public virtual void Remove(Object obj)
- {
- Contract.Ensures(Count >= 0);
-
- int index = IndexOf(obj);
- BCLDebug.Correctness(index >= 0 || !(obj is Int32), "You passed an Int32 to Remove that wasn't in the ArrayList." + Environment.NewLine + "Did you mean RemoveAt? int: " + obj + " Count: " + Count);
- if (index >= 0)
- RemoveAt(index);
- }
-
- // Removes the element at the given index. The size of the list is
- // decreased by one.
- //
- public virtual void RemoveAt(int index)
- {
- if (index < 0 || index >= _size) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
- Contract.Ensures(Count >= 0);
- //Contract.Ensures(Count == Contract.OldValue(Count) - 1);
- Contract.EndContractBlock();
-
- _size--;
- if (index < _size)
- {
- Array.Copy(_items, index + 1, _items, index, _size - index);
- }
- _items[_size] = null;
- _version++;
- }
-
- // ToArray returns a new array of a particular type containing the contents
- // of the ArrayList. This requires copying the ArrayList and potentially
- // downcasting all elements. This copy may fail and is an O(n) operation.
- // Internally, this implementation calls Array.Copy.
- //
- public virtual Array ToArray(Type type)
- {
- if (type == null)
- throw new ArgumentNullException(nameof(type));
- Contract.Ensures(Contract.Result<Array>() != null);
- Contract.EndContractBlock();
- Array array = Array.UnsafeCreateInstance(type, _size);
- Array.Copy(_items, 0, array, 0, _size);
- return array;
- }
-
- [Serializable]
- private class ReadOnlyList : IList
- {
- private IList _list;
-
- internal ReadOnlyList(IList l)
- {
- _list = l;
- }
-
- public virtual int Count
- {
- get { return _list.Count; }
- }
-
- public virtual bool IsReadOnly
- {
- get { return true; }
- }
-
- public virtual bool IsFixedSize
- {
- get { return true; }
- }
-
- public virtual bool IsSynchronized
- {
- get { return _list.IsSynchronized; }
- }
-
- public virtual Object this[int index]
- {
- get
- {
- return _list[index];
- }
- set
- {
- throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection);
- }
- }
-
- public virtual Object SyncRoot
- {
- get { return _list.SyncRoot; }
- }
-
- public virtual int Add(Object obj)
- {
- throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection);
- }
-
- public virtual void Clear()
- {
- throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection);
- }
-
- public virtual bool Contains(Object obj)
- {
- return _list.Contains(obj);
- }
-
- public virtual void CopyTo(Array array, int index)
- {
- _list.CopyTo(array, index);
- }
-
- public virtual IEnumerator GetEnumerator()
- {
- return _list.GetEnumerator();
- }
-
- public virtual int IndexOf(Object value)
- {
- return _list.IndexOf(value);
- }
-
- public virtual void Insert(int index, Object obj)
- {
- throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection);
- }
-
- public virtual void Remove(Object value)
- {
- throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection);
- }
-
- public virtual void RemoveAt(int index)
- {
- throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection);
- }
- }
-
- [Serializable]
- private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable
- {
- private ArrayList list;
- private int index;
- private int version;
- private Object currentElement;
- [NonSerialized]
- private bool isArrayList;
- // this object is used to indicate enumeration has not started or has terminated
- private static Object dummyObject = new Object();
-
- internal ArrayListEnumeratorSimple(ArrayList list)
- {
- this.list = list;
- index = -1;
- version = list._version;
- isArrayList = (list.GetType() == typeof(ArrayList));
- currentElement = dummyObject;
- }
-
- public Object Clone()
- {
- return MemberwiseClone();
- }
-
- public bool MoveNext()
- {
- if (version != list._version)
- {
- throw new InvalidOperationException(SR.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- }
-
- if (isArrayList)
- { // avoid calling virtual methods if we are operating on ArrayList to improve performance
- if (index < list._size - 1)
- {
- currentElement = list._items[++index];
- return true;
- }
- else
- {
- currentElement = dummyObject;
- index = list._size;
- return false;
- }
- }
- else
- {
- if (index < list.Count - 1)
- {
- currentElement = list[++index];
- return true;
- }
- else
- {
- index = list.Count;
- currentElement = dummyObject;
- return false;
- }
- }
- }
-
- public Object Current
- {
- get
- {
- object temp = currentElement;
- if (dummyObject == temp)
- { // check if enumeration has not started or has terminated
- if (index == -1)
- {
- throw new InvalidOperationException(SR.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
- }
- else
- {
- throw new InvalidOperationException(SR.GetResourceString(ResId.InvalidOperation_EnumEnded));
- }
- }
-
- return temp;
- }
- }
-
- public void Reset()
- {
- if (version != list._version)
- {
- throw new InvalidOperationException(SR.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
- }
-
- currentElement = dummyObject;
- index = -1;
- }
- }
-
- internal class ArrayListDebugView
- {
- private ArrayList arrayList;
- }
- }
-}
** thread safety. If a reader writer lock is available, then that may be used
** with a Dictionary to get the same thread safety guarantee.
**
-** Reader writer locks don't exist in silverlight, so we do the following as a
-** result of removing non-generic collections from silverlight:
-** 1. If the Hashtable was fully synchronized, then we replace it with a
-** Dictionary with full locks around reads/writes (same thread safety
-** guarantee).
-** 2. Otherwise, the Hashtable has the default MR/SW thread safety behavior,
-** so we do one of the following on a case-by-case basis:
-** a. If the race condition can be addressed by rearranging the code and using a temp
-** variable (for example, it's only populated immediately after created)
-** then we address the race condition this way and use Dictionary.
-** b. If there's concern about degrading performance with the increased
-** locking, we ifdef with FEATURE_NONGENERIC_COLLECTIONS so we can at
-** least use Hashtable in the desktop build, but Dictionary with full
-** locks in silverlight builds. Note that this is heavier locking than
-** MR/SW, but this is the only option without rewriting (or adding back)
-** the reader writer lock.
-** c. If there's no performance concern (e.g. debug-only code) we
-** consistently replace Hashtable with Dictionary plus full locks to
-** reduce complexity.
-** d. Most of serialization is dead code in silverlight. Instead of updating
-** those Hashtable occurences in serialization, we carved out references
-** to serialization such that this code doesn't need to build in
-** silverlight.
===========================================================*/
namespace System.Collections.Generic
value = default(TValue);
return false;
}
-
- // Method similar to TryGetValue that returns the value instead of putting it in an out param.
- public TValue GetValueOrDefault(TKey key) => GetValueOrDefault(key, default(TValue));
-
- // Method similar to TryGetValue that returns the value instead of putting it in an out param. If the entry
- // doesn't exist, returns the defaultValue instead.
- public TValue GetValueOrDefault(TKey key, TValue defaultValue)
- {
- int i = FindEntry(key);
- if (i >= 0)
- {
- return entries[i].value;
- }
- return defaultValue;
- }
-
public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None);
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
using System;
using System.Reflection;
-using System.Runtime.Remoting;
using System.Runtime.Serialization;
using System.Globalization;
using System.Diagnostics.Contracts;
// The Debugger class is a part of the System.Diagnostics package
// and is used for communicating with a debugger.
-using System;
-using System.IO;
-using System.Collections;
-using System.Reflection;
using System.Runtime.CompilerServices;
-using System.Security;
-using System.Runtime.Versioning;
namespace System.Diagnostics
{
- // No data, does not need to be marked with the serializable attribute
- public sealed class Debugger
+ public static class Debugger
{
- // This should have been a static class, but wasn't as of v3.5. Clearly, this is
- // broken. We'll keep this in V4 for binary compat, but marked obsolete as error
- // so migrated source code gets fixed.
- [Obsolete("Do not create instances of the Debugger class. Call the static methods directly on this type instead", true)]
- public Debugger()
- {
- // Should not have been instantiable - here for binary compatibility in V4.
- }
-
// Break causes a breakpoint to be signalled to an attached debugger. If no debugger
- // is attached, the user is asked if he wants to attach a debugger. If yes, then the
+ // is attached, the user is asked if he wants to attach a debugger. If yes, then the
// debugger is launched.
- [MethodImplAttribute(MethodImplOptions.NoInlining)]
- public static void Break()
- {
- // Causing a break is now allowed.
- BreakInternal();
- }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void Break() => BreakInternal();
- private static void BreakCanThrow()
- {
- BreakInternal();
- }
+ // The VM depends on this private method.
+ private static void BreakCanThrow() => BreakInternal();
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [MethodImpl(MethodImplOptions.InternalCall)]
private static extern void BreakInternal();
// Launch launches & attaches a debugger to the process. If a debugger is already attached,
- // nothing happens.
+ // nothing happens.
//
- public static bool Launch()
- {
- if (Debugger.IsAttached)
- return (true);
-
- // Causing the debugger to launch is now allowed.
- return (LaunchInternal());
- }
+ public static bool Launch() => IsAttached ? true : LaunchInternal();
// This class implements code:ICustomDebuggerNotification and provides a type to be used to notify
- // the debugger that execution is about to enter a path that involves a cross-thread dependency.
- // See code:NotifyOfCrossThreadDependency for more details.
- private class CrossThreadDependencyNotification : ICustomDebuggerNotification
- {
- // constructor
- public CrossThreadDependencyNotification()
- {
- }
- }
-
- // Do not inline the slow path
- [MethodImplAttribute(MethodImplOptions.NoInlining)]
- private static void NotifyOfCrossThreadDependencySlow()
- {
- CrossThreadDependencyNotification notification = new CrossThreadDependencyNotification();
- CustomNotification(notification);
- }
-
- // Sends a notification to the debugger to indicate that execution is about to enter a path
- // involving a cross thread dependency. A debugger that has opted into this type of notification
- // can take appropriate action on receipt. For example, performing a funceval normally requires
- // freezing all threads but the one performing the funceval. If the funceval requires execution on
- // more than one thread, as might occur in remoting scenarios, the funceval will block. This
- // notification will apprise the debugger that it will need to slip a thread or abort the funceval
- // in such a situation. The notification is subject to collection after this function returns.
- //
+ // the debugger that execution is about to enter a path that involves a cross-thread dependency.
+ // See code:NotifyOfCrossThreadDependency for more details.
+ private class CrossThreadDependencyNotification : ICustomDebuggerNotification { }
+
+ // Do not inline the slow path
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void NotifyOfCrossThreadDependencySlow() =>
+ CustomNotification(new CrossThreadDependencyNotification());
+
+ // Sends a notification to the debugger to indicate that execution is about to enter a path
+ // involving a cross thread dependency. A debugger that has opted into this type of notification
+ // can take appropriate action on receipt. For example, performing a funceval normally requires
+ // freezing all threads but the one performing the funceval. If the funceval requires execution on
+ // more than one thread, as might occur in remoting scenarios, the funceval will block. This
+ // notification will apprise the debugger that it will need to slip a thread or abort the funceval
+ // in such a situation. The notification is subject to collection after this function returns.
+ //
public static void NotifyOfCrossThreadDependency()
{
- if (Debugger.IsAttached)
+ if (IsAttached)
{
NotifyOfCrossThreadDependencySlow();
}
}
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool LaunchInternal();
// Returns whether or not a debugger is attached to the process.
//
public static extern bool IsAttached
{
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [MethodImpl(MethodImplOptions.InternalCall)]
get;
}
// An attached debugger can enable or disable which messages will
// actually be reported to the user through the COM+ debugger
// services API. This info is communicated to the runtime so only
- // desired events are actually reported to the debugger.
+ // desired events are actually reported to the debugger.
//
// Constant representing the default category
- public static readonly String DefaultCategory = null;
+ public static readonly string DefaultCategory = null;
// Posts a message for the attached debugger. If there is no
// debugger attached, has no effect. The debugger may or may not
- // report the message depending on its settings.
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- public static extern void Log(int level, String category, String message);
+ // report the message depending on its settings.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void Log(int level, string category, string message);
// Checks to see if an attached debugger has logging enabled
- //
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ //
+ [MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool IsLogging();
// Posts a custom notification for the attached debugger. If there is no
// debugger attached, has no effect. The debugger may or may not
- // report the notification depending on its settings.
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ // report the notification depending on its settings.
+ [MethodImpl(MethodImplOptions.InternalCall)]
private static extern void CustomNotification(ICustomDebuggerNotification data);
}
}
namespace System.Diagnostics
{
- using System.Runtime.Remoting;
using System;
using System.IO;
using System.Collections;
using System.Diagnostics.Contracts;
using System;
-using System.Runtime.Remoting;
using System.Runtime.Serialization;
namespace System
}
}
-
- private static volatile OperatingSystem m_os; // Cached OperatingSystem value
-
/*==================================TickCount===================================
**Action: Gets the number of ticks since the system was started.
**Returns: The number of ticks since the system was started.
}
}
- GCHandle contextHandle = GCHandle.Alloc(data);
- try
- {
- // Now call the enumeration API. Work is done by our callback function
-#if CORECLR
- Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarsCallback, localeName, ENUM_ALL_CALENDARS, null, CAL_ICALINTVALUE, (IntPtr)contextHandle);
-#else
- IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, IntPtr, Interop.BOOL>>(EnumCalendarsCallback);
- Interop.Kernel32.EnumCalendarInfoExEx(callback, localeName, ENUM_ALL_CALENDARS, null, CAL_ICALINTVALUE, (IntPtr)contextHandle);
-#endif
- }
- finally
+ unsafe
{
- contextHandle.Free();
+ Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarsCallback, localeName, ENUM_ALL_CALENDARS, null, CAL_ICALINTVALUE, Unsafe.AsPointer(ref data));
}
// Copy to the output array
}
// EnumCalendarInfoExEx callback itself.
-#if !CORECLR
- [NativeCallable(CallingConvention = CallingConvention.StdCall)]
-#endif
- private static unsafe Interop.BOOL EnumCalendarInfoCallback(IntPtr lpCalendarInfoString, uint calendar, IntPtr pReserved, IntPtr lParam)
+ // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
+ private static unsafe Interop.BOOL EnumCalendarInfoCallback(char* lpCalendarInfoString, uint calendar, IntPtr pReserved, void* lParam)
{
- EnumData context = (EnumData)((GCHandle)lParam).Target;
+ ref EnumData context = ref Unsafe.As<byte, EnumData>(ref *(byte*)lParam);
try
{
- string calendarInfo = new string((char*)lpCalendarInfoString);
+ string calendarInfo = new string(lpCalendarInfoString);
// If we had a user override, check to make sure this differs
if (context.userOverride != calendarInfo)
string res = CultureData.GetLocaleInfoEx(localeName, lcType);
// if it succeeded remember the override for the later callers
- if (res != "")
+ if (res != null)
{
// Remember this was the override (so we can look for duplicates later in the enum function)
context.userOverride = res;
}
}
- GCHandle contextHandle = GCHandle.Alloc(context);
- try
+ unsafe
{
-#if CORECLR
- Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, (uint)calendar, null, calType, (IntPtr)contextHandle);
-#else
// Now call the enumeration API. Work is done by our callback function
- IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, IntPtr, Interop.BOOL>>(EnumCalendarInfoCallback);
- Interop.Kernel32.EnumCalendarInfoExEx(callback, localeName, (uint)calendar, null, calType, (IntPtr)contextHandle);
-#endif // CORECLR
- }
- finally
- {
- contextHandle.Free();
+ Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, (uint)calendar, null, calType, Unsafe.AsPointer(ref context));
}
// Now we have a list of data, fail if we didn't find anything.
public IntList calendars; // list of calendars found so far
}
-#if !CORECLR
- [NativeCallable(CallingConvention = CallingConvention.StdCall)]
-#endif
- private static Interop.BOOL EnumCalendarsCallback(IntPtr lpCalendarInfoString, uint calendar, IntPtr reserved, IntPtr lParam)
+ // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
+ private static unsafe Interop.BOOL EnumCalendarsCallback(char* lpCalendarInfoString, uint calendar, IntPtr reserved, void* lParam)
{
- EnumCalendarsData context = (EnumCalendarsData)((GCHandle)lParam).Target;
+ ref EnumCalendarsData context = ref Unsafe.As<byte, EnumCalendarsData>(ref *(byte*)lParam);
try
{
// If we had a user override, check to make sure this differs
using System.Collections.Generic;
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
return new String(pBuffer);
}
- return "";
+ return null;
}
internal static unsafe int GetLocaleInfoExInt(String localeName, uint field)
{
Debug.Assert(!GlobalizationMode.Invariant);
- return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, (IntPtr)lpLCData, cchData);
+ return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, lpLCData, cchData);
}
private string GetLocaleInfo(LocaleStringData type)
context.cultureName = null;
context.regionName = regionName;
- GCHandle contextHandle = GCHandle.Alloc(context);
- try
+ unsafe
{
-#if CORECLR
- Interop.Kernel32.EnumSystemLocalesEx(EnumSystemLocalesProc, LOCALE_SPECIFICDATA | LOCALE_SUPPLEMENTAL, (IntPtr)contextHandle, IntPtr.Zero);
-#else
- IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, Interop.BOOL>>(EnumSystemLocalesProc);
- Interop.Kernel32.EnumSystemLocalesEx(callback, LOCALE_SPECIFICDATA | LOCALE_SUPPLEMENTAL, (IntPtr)contextHandle, IntPtr.Zero);
-#endif
- }
- finally
- {
- contextHandle.Free();
+ Interop.Kernel32.EnumSystemLocalesEx(EnumSystemLocalesProc, LOCALE_SPECIFICDATA | LOCALE_SUPPLEMENTAL, Unsafe.AsPointer(ref context), IntPtr.Zero);
}
if (context.cultureName != null)
}
// EnumSystemLocaleEx callback.
-#if !CORECLR
- [NativeCallable(CallingConvention = CallingConvention.StdCall)]
-#endif
- private static unsafe Interop.BOOL EnumSystemLocalesProc(IntPtr lpLocaleString, uint flags, IntPtr contextHandle)
+ // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
+ private static unsafe Interop.BOOL EnumSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
{
- EnumLocaleData context = (EnumLocaleData)((GCHandle)contextHandle).Target;
+ ref EnumLocaleData context = ref Unsafe.As<byte, EnumLocaleData>(ref *(byte*)contextHandle);
try
{
- string cultureName = new string((char*)lpLocaleString);
+ string cultureName = new string(lpLocaleString);
string regionName = GetLocaleInfoEx(cultureName, LOCALE_SISO3166CTRYNAME);
if (regionName != null && regionName.Equals(context.regionName, StringComparison.OrdinalIgnoreCase))
{
}
// EnumSystemLocaleEx callback.
-#if !CORECLR
- [NativeCallable(CallingConvention = CallingConvention.StdCall)]
-#endif
- private static unsafe Interop.BOOL EnumAllSystemLocalesProc(IntPtr lpLocaleString, uint flags, IntPtr contextHandle)
+ // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
+ private static unsafe Interop.BOOL EnumAllSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
{
- EnumData context = (EnumData)((GCHandle)contextHandle).Target;
+ ref EnumData context = ref Unsafe.As<byte, EnumData>(ref *(byte*)contextHandle);
try
{
- context.strings.Add(new string((char*)lpLocaleString));
+ context.strings.Add(new string(lpLocaleString));
return Interop.BOOL.TRUE;
}
catch (Exception)
}
// EnumTimeFormatsEx callback itself.
-#if !CORECLR
- [NativeCallable(CallingConvention = CallingConvention.StdCall)]
-#endif
- private static unsafe Interop.BOOL EnumTimeCallback(IntPtr lpTimeFormatString, IntPtr lParam)
+ // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
+ private static unsafe Interop.BOOL EnumTimeCallback(char* lpTimeFormatString, void* lParam)
{
- EnumData context = (EnumData)((GCHandle)lParam).Target;
-
+ ref EnumData context = ref Unsafe.As<byte, EnumData>(ref *(byte*)lParam);
try
{
- context.strings.Add(new string((char*)lpTimeFormatString));
+ context.strings.Add(new string(lpTimeFormatString));
return Interop.BOOL.TRUE;
}
catch (Exception)
EnumData data = new EnumData();
data.strings = new StringList();
- GCHandle dataHandle = GCHandle.Alloc(data);
- try
- {
-#if CORECLR
- Interop.Kernel32.EnumTimeFormatsEx(EnumTimeCallback, localeName, (uint)dwFlags, (IntPtr)dataHandle);
-#else
- // Now call the enumeration API. Work is done by our callback function
- IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, IntPtr, Interop.BOOL>>(EnumTimeCallback);
- Interop.Kernel32.EnumTimeFormatsEx(callback, localeName, (uint)dwFlags, (IntPtr)dataHandle);
-#endif
- }
- finally
- {
- dataHandle.Free();
- }
+
+ // Now call the enumeration API. Work is done by our callback function
+ Interop.Kernel32.EnumTimeFormatsEx(EnumTimeCallback, localeName, (uint)dwFlags, Unsafe.AsPointer(ref data));
if (data.strings.Count > 0)
{
EnumData context = new EnumData();
context.strings = new StringList();
- GCHandle contextHandle = GCHandle.Alloc(context);
- try
- {
-#if CORECLR
- Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, flags, (IntPtr)contextHandle, IntPtr.Zero);
-#else
- IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, Interop.BOOL>>(EnumAllSystemLocalesProc);
- Interop.Kernel32.EnumSystemLocalesEx(callback, flags, (IntPtr)contextHandle, IntPtr.Zero);
-#endif
- }
- finally
+
+ unsafe
{
- contextHandle.Free();
+ Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, flags, Unsafe.AsPointer(ref context), IntPtr.Zero);
}
CultureInfo [] cultures = new CultureInfo[context.strings.Count];
{
EnumData context = new EnumData();
context.strings = new StringList();
- GCHandle contextHandle = GCHandle.Alloc(context);
- try
- {
-#if CORECLR
- Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT, (IntPtr)contextHandle, IntPtr.Zero);
-#else
- IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, Interop.BOOL>>(EnumAllSystemLocalesProc);
- Interop.Kernel32.EnumSystemLocalesEx(callback, Interop.Kernel32.LOCALE_REPLACEMENT, (IntPtr)contextHandle, IntPtr.Zero);
-#endif
- }
- finally
+
+ unsafe
{
- contextHandle.Free();
+ Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT, Unsafe.AsPointer(ref context), IntPtr.Zero);
}
for (int i=0; i<context.strings.Count; i++)
// See the LICENSE file in the project root for more information.
using System.Text;
-using System.Runtime.Remoting;
using System;
using System.Security;
{
throw new NotSupportedException(SR.NotSupported_UnwritableStream);
}
-
- // From WinError.h
- internal const int ERROR_FILE_NOT_FOUND = Win32Native.ERROR_FILE_NOT_FOUND;
- internal const int ERROR_PATH_NOT_FOUND = Win32Native.ERROR_PATH_NOT_FOUND;
- internal const int ERROR_ACCESS_DENIED = Win32Native.ERROR_ACCESS_DENIED;
- internal const int ERROR_INVALID_PARAMETER = Win32Native.ERROR_INVALID_PARAMETER;
}
}
using System;
-using System.Runtime.Remoting;
using System.Runtime.Serialization;
using System.Runtime.CompilerServices;
using System.Globalization;
using System;
-using System.Runtime.Remoting;
using System.Runtime.Serialization;
using System.Runtime.CompilerServices;
using System.Globalization;
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-** Purpose:
-**
-**
-===========================================================*/
-
-using System.Runtime.Serialization;
-using System.Globalization;
-using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
-
-namespace System
-{
- [Serializable]
- internal sealed class OperatingSystem : ICloneable, ISerializable
- {
- private Version _version;
- private PlatformID _platform;
- private string _servicePack;
- private string _versionString;
-
- private OperatingSystem()
- {
- }
-
- internal OperatingSystem(PlatformID platform, Version version, string servicePack)
- {
- if (platform < PlatformID.Win32S || platform > PlatformID.MacOSX)
- {
- throw new ArgumentException(
- SR.Format(SR.Arg_EnumIllegalVal, (int)platform),
- nameof(platform));
- }
-
- if ((Object)version == null)
- throw new ArgumentNullException(nameof(version));
- Contract.EndContractBlock();
-
- _platform = platform;
- _version = (Version)version.Clone();
- _servicePack = servicePack;
- }
-
- private OperatingSystem(SerializationInfo info, StreamingContext context)
- {
- SerializationInfoEnumerator enumerator = info.GetEnumerator();
- while (enumerator.MoveNext())
- {
- switch (enumerator.Name)
- {
- case "_version":
- _version = (Version)info.GetValue("_version", typeof(Version));
- break;
- case "_platform":
- _platform = (PlatformID)info.GetValue("_platform", typeof(PlatformID));
- break;
- case "_servicePack":
- _servicePack = info.GetString("_servicePack");
- break;
- }
- }
-
- if (_version == null)
- {
- throw new SerializationException(SR.Format(SR.Serialization_MissField, "_version"));
- }
- }
-
- public void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
- Contract.EndContractBlock();
-
- info.AddValue("_version", _version);
- info.AddValue("_platform", _platform);
- info.AddValue("_servicePack", _servicePack);
- }
-
- public Version Version
- {
- get { return _version; }
- }
-
- public Object Clone()
- {
- return new OperatingSystem(_platform,
- _version, _servicePack);
- }
-
- public override String ToString()
- {
- return VersionString;
- }
-
- public String VersionString
- {
- get
- {
- if (_versionString != null)
- {
- return _versionString;
- }
-
- String os;
- switch (_platform)
- {
- case PlatformID.Win32NT:
- os = "Microsoft Windows NT ";
- break;
- case PlatformID.Win32Windows:
- if ((_version.Major > 4) ||
- ((_version.Major == 4) && (_version.Minor > 0)))
- os = "Microsoft Windows 98 ";
- else
- os = "Microsoft Windows 95 ";
- break;
- case PlatformID.Win32S:
- os = "Microsoft Win32S ";
- break;
- case PlatformID.WinCE:
- os = "Microsoft Windows CE ";
- break;
- case PlatformID.MacOSX:
- os = "Mac OS X ";
- break;
- default:
- os = "<unknown> ";
- break;
- }
-
- if (String.IsNullOrEmpty(_servicePack))
- {
- _versionString = os + _version.ToString();
- }
- else
- {
- _versionString = os + _version.ToString(3) + " " + _servicePack;
- }
-
- return _versionString;
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-** Purpose: Defines IDs for supported platforms
-**
-**
-===========================================================*/
-
-namespace System
-{
- [Serializable]
- internal enum PlatformID
- {
- Win32S = 0,
- Win32Windows = 1,
- Win32NT = 2,
- WinCE = 3,
- Unix = 4,
- Xbox = 5,
- MacOSX = 6
- }
-}
return AssemblyLoadContext.Default.LoadFromAssemblyPath(fullPath);
}
- // Evidence is protected in Assembly.Load()
- [Obsolete("This method is obsolete and will be removed in a future release of the .NET Framework. Please use an overload of LoadFrom which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")]
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
- internal static Assembly LoadFrom(String assemblyFile,
- Evidence securityEvidence)
- {
- Contract.Ensures(Contract.Result<Assembly>() != null);
-
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
-
- return RuntimeAssembly.InternalLoadFrom(
- assemblyFile,
- securityEvidence,
- null, // hashValue
- AssemblyHashAlgorithm.None,
- false,// forIntrospection);
- ref stackMark);
- }
-
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
public static Assembly LoadFrom(String assemblyFile,
byte[] hashValue,
AssemblyHashAlgorithm hashAlgorithm)
count = 0;
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
return Array.Empty<Attribute>();
List<Attribute> pcas = new List<Attribute>();
internal static bool IsDefined(RuntimeType type, RuntimeType caType)
{
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
return false;
if (all || caType == (RuntimeType)typeof(SerializableAttribute))
count = 0;
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
return Array.Empty<Attribute>();
List<Attribute> pcas = new List<Attribute>();
internal static bool IsDefined(RuntimeMethodInfo method, RuntimeType caType)
{
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null)
+ if (!all && !s_pca.ContainsKey(caType))
return false;
if (all || caType == (RuntimeType)typeof(DllImportAttribute))
count = 0;
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null)
+ if (!all && !s_pca.ContainsKey(caType))
return null;
Attribute[] pcas = new Attribute[s_pcasCount];
internal static bool IsDefined(RuntimeParameterInfo parameter, RuntimeType caType)
{
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null)
+ if (!all && !s_pca.ContainsKey(caType))
return false;
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
return Array.Empty<Attribute>();
List<Attribute> pcas = new List<Attribute>();
count = 0;
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null)
+ if (!all && !s_pca.ContainsKey(caType))
return null;
Attribute[] pcas = new Attribute[s_pcasCount];
internal static bool IsDefined(RuntimeFieldInfo field, RuntimeType caType)
{
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null)
+ if (!all && !s_pca.ContainsKey(caType))
return false;
if (all || caType == (RuntimeType)typeof(MarshalAsAttribute))
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
return Array.Empty<Attribute>();
List<Attribute> pcas = new List<Attribute>();
{
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && s_pca.GetValueOrDefault(caType) == null)
+ if (!all && !s_pca.ContainsKey(caType))
return false;
if (all || IsSecurityAttribute(caType))
return CustomAttributeData.GetCustomAttributesInternal(this);
}
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
- internal static RuntimeAssembly InternalLoadFrom(String assemblyFile,
- Evidence securityEvidence,
- byte[] hashValue,
- AssemblyHashAlgorithm hashAlgorithm,
- bool forIntrospection,
- ref StackCrawlMark stackMark)
- {
- if (assemblyFile == null)
- throw new ArgumentNullException(nameof(assemblyFile));
-
- Contract.EndContractBlock();
-
- AssemblyName an = new AssemblyName();
- an.CodeBase = assemblyFile;
- an.SetHashControl(hashValue, hashAlgorithm);
- // The stack mark is used for MDA filtering
- return InternalLoadAssemblyName(an, securityEvidence, null, ref stackMark, true /*thrownOnFileNotFound*/, forIntrospection);
- }
-
// Wrapper function to wrap the typical use of InternalLoad.
internal static RuntimeAssembly InternalLoad(String assemblyString,
Evidence assemblySecurity,
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
-using System.Runtime.Remoting;
using MdSigCallingConvention = System.Signature.MdSigCallingConvention;
using RuntimeTypeCache = System.RuntimeType.RuntimeTypeCache;
using System.Runtime.InteropServices;
{
string name = eventInfo.Name;
- if (csEventInfos.GetValueOrDefault(name) != null)
+ if (csEventInfos.ContainsKey(name))
continue;
csEventInfos[name] = eventInfo;
if (csPropertyInfos != null)
{
string name = propertyInfo.Name;
-
- List<RuntimePropertyInfo> cache = csPropertyInfos.GetValueOrDefault(name);
-
- if (cache == null)
+ List<RuntimePropertyInfo> cache;
+ if (!csPropertyInfos.TryGetValue(name, out cache))
{
cache = new List<RuntimePropertyInfo>(1);
csPropertyInfos[name] = cache;
using System;
using System.Runtime.Serialization;
-using System.Runtime.Remoting;
using System.Diagnostics.Contracts;
namespace System.Runtime.CompilerServices
// code:ComEventsMethod.Invoke
using System;
-using System.Runtime.Remoting;
namespace System.Runtime.InteropServices
{
using System.Security;
using System.Text;
using System.Threading;
- using System.Runtime.Remoting;
using System.Runtime.CompilerServices;
using System.Globalization;
using System.Runtime.ConstrainedExecution;
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-** ObjectHandle wraps object references. A Handle allows a
-** marshal by value object to be returned through an
-** indirection allowing the caller to control when the
-** object is loaded into their domain.
-**
-**
-===========================================================*/
-
-using System;
-using System.Runtime.InteropServices;
-
-namespace System.Runtime.Remoting
-{
- public class ObjectHandle
- {
- private Object WrappedObject;
-
- private ObjectHandle()
- {
- }
-
- internal ObjectHandle(Object o)
- {
- WrappedObject = o;
- }
-
- internal Object Unwrap()
- {
- return WrappedObject;
- }
- }
-}
using System.Collections;
using System.Collections.Generic;
using System.Security;
-using System.Runtime.Remoting;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using System;
using System.Collections.Generic;
using System.Reflection;
-using System.Runtime.Remoting;
using System.Globalization;
using System.Diagnostics;
using System.Diagnostics.Contracts;
=============================================================================*/
using System.Threading;
-using System.Runtime.Remoting;
using System.Security;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Collections;
using System.Collections.Generic;
using System.Runtime;
- using System.Runtime.Remoting;
using System.Runtime.Serialization;
using System.Globalization;
using System.Security;
using System;
using System.Runtime;
-using System.Runtime.Remoting;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
namespace System.Threading
{
using System.Threading;
- using System.Runtime.Remoting;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System;
using System.Globalization;
-using System.Runtime.Remoting;
using System.Runtime.Serialization;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
PALIMPORT
LPVOID
PALAPI
+PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(
+ IN LPCVOID lpBeginAddress,
+ IN LPCVOID lpEndAddress,
+ IN SIZE_T dwSize);
+
+PALIMPORT
+LPVOID
+PALAPI
VirtualAlloc(
IN LPVOID lpAddress,
IN SIZE_T dwSize,
set(PAL_ARCH_SOURCES_DIR i386)
endif()
-if(CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+if(PAL_CMAKE_PLATFORM_ARCH_AMD64 AND CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX)
# Currently the _xstate is not available on Alpine Linux
add_definitions(-DXSTATE_SUPPORTED)
-endif(CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+endif(PAL_CMAKE_PLATFORM_ARCH_AMD64 AND CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX)
if(CLR_CMAKE_PLATFORM_ALPINE_LINUX)
# Setting RLIMIT_NOFILE breaks debugging of coreclr on Alpine Linux for some reason
#cmakedefine HAVE_UNW_GET_ACCESSORS
#cmakedefine01 HAVE_XSWDEV
#cmakedefine01 HAVE_XSW_USAGE
+#cmakedefine01 HAVE_PUBLIC_XSTATE_STRUCT
+#cmakedefine01 HAVE_PR_SET_PTRACER
#cmakedefine01 HAVE_STAT_TIMESPEC
#cmakedefine01 HAVE_STAT_NSEC
return 0;
}" HAVE_XSW_USAGE)
+check_cxx_source_compiles("
+#include <signal.h>
+
+int main(int argc, char **argv)
+{
+ struct _xstate xstate;
+ struct _fpx_sw_bytes bytes;
+ return 0;
+}" HAVE_PUBLIC_XSTATE_STRUCT)
+
+check_cxx_source_compiles("
+#include <sys/prctl.h>
+
+int main(int argc, char **argv)
+{
+ int flag = (int)PR_SET_PTRACER;
+ return 0;
+}" HAVE_PR_SET_PTRACER)
+
set(CMAKE_REQUIRED_LIBRARIES pthread)
check_cxx_source_compiles("
#include <errno.h>
#else // HAVE_UCONTEXT_T
#error Native context type is not known on this platform!
#endif // HAVE_UCONTEXT_T
+
+#if defined(XSTATE_SUPPORTED) && !HAVE_PUBLIC_XSTATE_STRUCT
+namespace asm_sigcontext
+{
+#include <asm/sigcontext.h>
+};
+using asm_sigcontext::_fpx_sw_bytes;
+using asm_sigcontext::_xstate;
+#endif // defined(XSTATE_SUPPORTED) && !HAVE_PUBLIC_XSTATE_STRUCT
+
#else // !HAVE_MACH_EXCEPTIONS
#include <mach/kern_return.h>
#include <mach/mach_port.h>
VIRTUAL_PAGE_SIZE = 0x1000,
VIRTUAL_PAGE_MASK = VIRTUAL_PAGE_SIZE - 1,
- BOUNDARY_64K = 0xffff
+ VIRTUAL_64KB = 0x10000
};
/*++
AllocateMemory
This function attempts to allocate the requested amount of memory from its reserved virtual
- address space. The function will return NULL if the allocation request cannot
+ address space. The function will return null if the allocation request cannot
be satisfied by the memory that is currently available in the allocator.
--*/
void* AllocateMemory(SIZE_T allocationSize);
+ /*++
+ Function:
+ AllocateMemory
+
+ This function attempts to allocate the requested amount of memory from its reserved virtual
+ address space, if memory is available within the specified range. The function will return
+ null if the allocation request cannot satisfied by the memory that is currently available in
+ the allocator.
+ --*/
+ void *AllocateMemoryWithinRange(const void *beginAddress, const void *endAddress, SIZE_T allocationSize);
+
private:
/*++
Function:
// that can be used to calculate an approximate location of the memory that
// is in 2GB range from the coreclr library. In addition, having precise size of libcoreclr
// is not necessary for the calculations.
- const int32_t CoreClrLibrarySize = 100 * 1024 * 1024;
+ static const int32_t CoreClrLibrarySize = 100 * 1024 * 1024;
// This constant represent the max size of the virtual memory that this allocator
// will try to reserve during initialization. We want all JIT-ed code and the
// entire libcoreclr to be located in a 2GB range.
- const int32_t MaxExecutableMemorySize = 0x7FFF0000 - CoreClrLibrarySize;
+ static const int32_t MaxExecutableMemorySize = 0x7FFF0000;
+ static const int32_t MaxExecutableMemorySizeNearCoreClr = MaxExecutableMemorySize - CoreClrLibrarySize;
// Start address of the reserved virtual address space
void* m_startAddress;
// We're going to start adding mappings to the mapping list, so take the critical section
InternalEnterCriticalSection(pThread, &mapping_critsec);
-#if !defined(_AMD64_)
- loadedBase = mmap((void*)preferredBase, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
-#else // defined(_AMD64_)
+#ifdef BIT64
// First try to reserve virtual memory using ExecutableAllcator. This allows all PE images to be
// near each other and close to the coreclr library which also allows the runtime to generate
- // more efficient code (by avoiding usage of jump stubs).
- loadedBase = ReserveMemoryFromExecutableAllocator(pThread, ALIGN_UP(virtualSize, GetVirtualPageSize()));
+ // more efficient code (by avoiding usage of jump stubs). Alignment to a 64 KB granularity should
+ // not be necessary (alignment to page size should be sufficient), but see
+ // ExecutableMemoryAllocator::AllocateMemory() for the reason why it is done.
+ loadedBase = ReserveMemoryFromExecutableAllocator(pThread, ALIGN_UP(virtualSize, VIRTUAL_64KB));
+#endif // BIT64
+
if (loadedBase == NULL)
{
// MAC64 requires we pass MAP_SHARED (or MAP_PRIVATE) flags - otherwise, the call is failed.
// Refer to mmap documentation at http://www.manpagez.com/man/2/mmap/ for details.
- loadedBase = mmap((void*)preferredBase, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
+ loadedBase = mmap(NULL, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
}
-#endif // !defined(_AMD64_)
if (MAP_FAILED == loadedBase)
{
--*/
+#include "pal/dbgmsg.h"
+
+SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL); // some headers have code with asserts, so do this first
+
#include "pal/thread.hpp"
#include "pal/cs.hpp"
#include "pal/malloc.hpp"
#include "pal/file.hpp"
#include "pal/seh.hpp"
-#include "pal/dbgmsg.h"
#include "pal/virtual.h"
#include "pal/map.h"
#include "pal/init.h"
+#include "pal/utils.h"
#include "common.h"
#include <sys/types.h>
using namespace CorUnix;
-SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL);
-
CRITICAL_SECTION virtual_critsec;
// The first node in our list of allocated blocks.
Decommit = 0x40,
Release = 0x50,
Reset = 0x60,
+ ReserveFromExecutableMemoryAllocatorWithinRange = 0x70
};
// Indicates that the attempted operation has failed
// First, figure out where we're trying to reserve the memory and
// how much we need. On most systems, requests to mmap must be
- // page-aligned and at multiples of the page size.
- StartBoundary = (UINT_PTR)lpAddress & ~BOUNDARY_64K;
+ // page-aligned and at multiples of the page size. Unlike on Windows, on
+ // Unix, the allocation granularity is the page size, so the memory size to
+ // reserve is not aligned to 64 KB. Nor should the start boundary need to
+ // to be aligned down to 64 KB, but it is expected that there are other
+ // components that rely on this alignment when providing a specific address
+ // (note that mmap itself does not make any such guarantees).
+ StartBoundary = (UINT_PTR)ALIGN_DOWN(lpAddress, VIRTUAL_64KB);
/* Add the sizes, and round down to the nearest page boundary. */
MemSize = ( ((UINT_PTR)lpAddress + dwSize + VIRTUAL_PAGE_MASK) & ~VIRTUAL_PAGE_MASK ) -
StartBoundary;
// try to get memory from the executable memory allocator to satisfy the request.
if (((flAllocationType & MEM_RESERVE_EXECUTABLE) != 0) && (lpAddress == NULL))
{
- pRetVal = g_executableMemoryAllocator.AllocateMemory(MemSize);
+ // Alignment to a 64 KB granularity should not be necessary (alignment to page size should be sufficient), but see
+ // ExecutableMemoryAllocator::AllocateMemory() for the reason why it is done
+ SIZE_T reservationSize = ALIGN_UP(MemSize, VIRTUAL_64KB);
+ pRetVal = g_executableMemoryAllocator.AllocateMemory(reservationSize);
+ if (pRetVal != nullptr)
+ {
+ MemSize = reservationSize;
+ }
}
if (pRetVal == NULL)
/*++
Function:
+ PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange
+
+ This function attempts to allocate the requested amount of memory in the specified address range, from the executable memory
+ allocator. If unable to do so, the function returns nullptr and does not set the last error.
+
+ lpBeginAddress - Inclusive beginning of range
+ lpEndAddress - Exclusive end of range
+ dwSize - Number of bytes to allocate
+--*/
+LPVOID
+PALAPI
+PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(
+ IN LPCVOID lpBeginAddress,
+ IN LPCVOID lpEndAddress,
+ IN SIZE_T dwSize)
+{
+#ifdef BIT64
+ PERF_ENTRY(PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange);
+ ENTRY(
+ "PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(lpBeginAddress = %p, lpEndAddress = %p, dwSize = %Iu)\n",
+ lpBeginAddress,
+ lpEndAddress,
+ dwSize);
+
+ _ASSERTE(lpBeginAddress <= lpEndAddress);
+
+ // Alignment to a 64 KB granularity should not be necessary (alignment to page size should be sufficient), but see
+ // ExecutableMemoryAllocator::AllocateMemory() for the reason why it is done
+ SIZE_T reservationSize = ALIGN_UP(dwSize, VIRTUAL_64KB);
+
+ CPalThread *currentThread = InternalGetCurrentThread();
+ InternalEnterCriticalSection(currentThread, &virtual_critsec);
+
+ void *address = g_executableMemoryAllocator.AllocateMemoryWithinRange(lpBeginAddress, lpEndAddress, reservationSize);
+ if (address != nullptr)
+ {
+ _ASSERTE(IS_ALIGNED(address, VIRTUAL_PAGE_SIZE));
+ if (!VIRTUALStoreAllocationInfo((UINT_PTR)address, reservationSize, MEM_RESERVE | MEM_RESERVE_EXECUTABLE, PAGE_NOACCESS))
+ {
+ ASSERT("Unable to store the structure in the list.\n");
+ munmap(address, reservationSize);
+ address = nullptr;
+ }
+ }
+
+ LogVaOperation(
+ VirtualMemoryLogging::VirtualOperation::ReserveFromExecutableMemoryAllocatorWithinRange,
+ nullptr,
+ dwSize,
+ MEM_RESERVE | MEM_RESERVE_EXECUTABLE,
+ PAGE_NOACCESS,
+ address,
+ TRUE);
+
+ InternalLeaveCriticalSection(currentThread, &virtual_critsec);
+
+ LOGEXIT("PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange returning %p\n", address);
+ PERF_EXIT(PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange);
+ return address;
+#else // !BIT64
+ return nullptr;
+#endif // BIT64
+}
+
+/*++
+Function:
VirtualAlloc
Note:
--*/
void* ReserveMemoryFromExecutableAllocator(CPalThread* pThread, SIZE_T allocationSize)
{
+#ifdef BIT64
InternalEnterCriticalSection(pThread, &virtual_critsec);
void* mem = g_executableMemoryAllocator.AllocateMemory(allocationSize);
InternalLeaveCriticalSection(pThread, &virtual_critsec);
return mem;
+#else // !BIT64
+ return nullptr;
+#endif // BIT64
}
/*++
void ExecutableMemoryAllocator::TryReserveInitialMemory()
{
CPalThread* pthrCurrent = InternalGetCurrentThread();
- int32_t sizeOfAllocation = MaxExecutableMemorySize;
- int32_t startAddressIncrement;
- UINT_PTR startAddress;
+ int32_t sizeOfAllocation = MaxExecutableMemorySizeNearCoreClr;
+ int32_t preferredStartAddressIncrement;
+ UINT_PTR preferredStartAddress;
UINT_PTR coreclrLoadAddress;
const int32_t MemoryProbingIncrement = 128 * 1024 * 1024;
// Try to find and reserve an available region of virtual memory that is located
- // within 2GB range (defined by the MaxExecutableMemorySize constant) from the
+ // within 2GB range (defined by the MaxExecutableMemorySizeNearCoreClr constant) from the
// location of the coreclr library.
// Potentially, as a possible future improvement, we can get precise information
// about available memory ranges by parsing data from '/proc/self/maps'.
// (thus avoiding reserving memory below 4GB; besides some operating systems do not allow that).
// If libcoreclr is loaded at high addresses then try to reserve memory below its location.
coreclrLoadAddress = (UINT_PTR)PAL_GetSymbolModuleBase((void*)VirtualAlloc);
- if ((coreclrLoadAddress < 0xFFFFFFFF) || ((coreclrLoadAddress - MaxExecutableMemorySize) < 0xFFFFFFFF))
+ if ((coreclrLoadAddress < 0xFFFFFFFF) || ((coreclrLoadAddress - MaxExecutableMemorySizeNearCoreClr) < 0xFFFFFFFF))
{
// Try to allocate above the location of libcoreclr
- startAddress = coreclrLoadAddress + CoreClrLibrarySize;
- startAddressIncrement = MemoryProbingIncrement;
+ preferredStartAddress = coreclrLoadAddress + CoreClrLibrarySize;
+ preferredStartAddressIncrement = MemoryProbingIncrement;
}
else
{
// Try to allocate below the location of libcoreclr
- startAddress = coreclrLoadAddress - MaxExecutableMemorySize;
- startAddressIncrement = 0;
+ preferredStartAddress = coreclrLoadAddress - MaxExecutableMemorySizeNearCoreClr;
+ preferredStartAddressIncrement = 0;
}
// Do actual memory reservation.
do
{
- m_startAddress = ReserveVirtualMemory(pthrCurrent, (void*)startAddress, sizeOfAllocation);
- if (m_startAddress != NULL)
+ m_startAddress = ReserveVirtualMemory(pthrCurrent, (void*)preferredStartAddress, sizeOfAllocation);
+ if (m_startAddress != nullptr)
{
- // Memory has been successfully reserved.
- m_totalSizeOfReservedMemory = sizeOfAllocation;
-
- // Randomize the location at which we start allocating from the reserved memory range.
- int32_t randomOffset = GenerateRandomStartOffset();
- m_nextFreeAddress = (void*)(((UINT_PTR)m_startAddress) + randomOffset);
- m_remainingReservedMemory = sizeOfAllocation - randomOffset;
break;
}
// Try to allocate a smaller region
sizeOfAllocation -= MemoryProbingIncrement;
- startAddress += startAddressIncrement;
+ preferredStartAddress += preferredStartAddressIncrement;
} while (sizeOfAllocation >= MemoryProbingIncrement);
+
+ if (m_startAddress == nullptr)
+ {
+ // We were not able to reserve any memory near libcoreclr. Try to reserve approximately 2 GB of address space somewhere
+ // anyway:
+ // - This sets aside address space that can be used for executable code, such that jumps/calls between such code may
+ // continue to use short relative addresses instead of long absolute addresses that would currently require jump
+ // stubs.
+ // - The inability to allocate memory in a specific range for jump stubs is an unrecoverable problem. This reservation
+ // would mitigate such issues that can become prevalent depending on which security features are enabled and to what
+ // extent, such as in particular, PaX's RANDMMAP:
+ // - https://en.wikibooks.org/wiki/Grsecurity/Appendix/Grsecurity_and_PaX_Configuration_Options
+ // - Jump stubs for executable code residing in this region can request memory from this allocator
+ // - Native images can be loaded into this address space, including any jump stubs that are required for its helper
+ // table. This satisfies the vast majority of practical cases where the total amount of loaded native image memory
+ // does not exceed approximately 2 GB.
+ // - The code heap allocator for the JIT can allocate from this address space. Beyond this reservation, one can use
+ // the COMPlus_CodeHeapReserveForJumpStubs environment variable to reserve space for jump stubs.
+ sizeOfAllocation = MaxExecutableMemorySize;
+ m_startAddress = ReserveVirtualMemory(pthrCurrent, nullptr, sizeOfAllocation);
+ if (m_startAddress == nullptr)
+ {
+ return;
+ }
+ }
+
+ // Memory has been successfully reserved.
+ m_totalSizeOfReservedMemory = sizeOfAllocation;
+
+ // Randomize the location at which we start allocating from the reserved memory range. Alignment to a 64 KB granularity
+ // should not be necessary, but see AllocateMemory() for the reason why it is done.
+ int32_t randomOffset = GenerateRandomStartOffset();
+ m_nextFreeAddress = ALIGN_UP((void*)(((UINT_PTR)m_startAddress) + randomOffset), VIRTUAL_64KB);
+ _ASSERTE(sizeOfAllocation >= (UINT_PTR)m_nextFreeAddress - (UINT_PTR)m_startAddress);
+ m_remainingReservedMemory =
+ ALIGN_DOWN(sizeOfAllocation - ((UINT_PTR)m_nextFreeAddress - (UINT_PTR)m_startAddress), VIRTUAL_64KB);
}
/*++
ExecutableMemoryAllocator::AllocateMemory
This function attempts to allocate the requested amount of memory from its reserved virtual
- address space. The function will return NULL if the allocation request cannot
+ address space. The function will return null if the allocation request cannot
be satisfied by the memory that is currently available in the allocator.
Note: This function MUST be called with the virtual_critsec lock held.
--*/
void* ExecutableMemoryAllocator::AllocateMemory(SIZE_T allocationSize)
{
- void* allocatedMemory = NULL;
+#ifdef BIT64
+ void* allocatedMemory = nullptr;
- // Allocation size must be in multiples of the virtual page size.
- _ASSERTE((allocationSize & VIRTUAL_PAGE_MASK) == 0);
+ // Alignment to a 64 KB granularity should not be necessary (alignment to page size should be sufficient), but
+ // VIRTUALReserveMemory() aligns down the specified address to a 64 KB granularity, and as long as that is necessary, the
+ // reservation size here must be aligned to a 64 KB granularity to guarantee that all returned addresses are also aligned to
+ // a 64 KB granularity. Otherwise, attempting to reserve memory starting from an unaligned address returned by this function
+ // would fail in VIRTUALReserveMemory.
+ _ASSERTE(IS_ALIGNED(allocationSize, VIRTUAL_64KB));
// The code below assumes that the caller owns the virtual_critsec lock.
// So the calculations are not done in thread-safe manner.
allocatedMemory = m_nextFreeAddress;
m_nextFreeAddress = (void*)(((UINT_PTR)m_nextFreeAddress) + allocationSize);
m_remainingReservedMemory -= allocationSize;
-
}
return allocatedMemory;
+#else // !BIT64
+ return nullptr;
+#endif // BIT64
+}
+
+/*++
+Function:
+ AllocateMemory
+
+ This function attempts to allocate the requested amount of memory from its reserved virtual
+ address space, if memory is available within the specified range. The function will return
+ null if the allocation request cannot satisfied by the memory that is currently available in
+ the allocator.
+
+ Note: This function MUST be called with the virtual_critsec lock held.
+--*/
+void *ExecutableMemoryAllocator::AllocateMemoryWithinRange(const void *beginAddress, const void *endAddress, SIZE_T allocationSize)
+{
+#ifdef BIT64
+ _ASSERTE(beginAddress <= endAddress);
+
+ // Alignment to a 64 KB granularity should not be necessary (alignment to page size should be sufficient), but see
+ // AllocateMemory() for the reason why it is necessary
+ _ASSERTE(IS_ALIGNED(allocationSize, VIRTUAL_64KB));
+
+ // The code below assumes that the caller owns the virtual_critsec lock.
+ // So the calculations are not done in thread-safe manner.
+
+ if (allocationSize == 0 || allocationSize > m_remainingReservedMemory)
+ {
+ return nullptr;
+ }
+
+ void *address = m_nextFreeAddress;
+ if (address < beginAddress)
+ {
+ return nullptr;
+ }
+
+ void *nextFreeAddress = (void *)((UINT_PTR)address + allocationSize);
+ if (nextFreeAddress > endAddress)
+ {
+ return nullptr;
+ }
+
+ m_nextFreeAddress = nextFreeAddress;
+ m_remainingReservedMemory -= allocationSize;
+ return address;
+#else // !BIT64
+ return nullptr;
+#endif // BIT64
}
/*++
// Do any shutdown cleanup before aborting or creating a core dump
PROCNotifyProcessShutdown();
-#if HAVE_PRCTL_H
+#if HAVE_PRCTL_H && HAVE_PR_SET_PTRACER
// If enabled, launch the create minidump utility and wait until it completes
if (g_argvCreateDump[0] != nullptr)
{
}
}
}
-#endif // HAVE_PRCTL_H
+#endif // HAVE_PRCTL_H && HAVE_PR_SET_PTRACER
// Abort the process after waiting for the core dump to complete
abort();
}
--- /dev/null
+from __future__ import print_function
+from genXplatEventing import *
+from genXplatLttng import *
+import os
+import xml.dom.minidom as DOM
+
+stdprolog = """// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************
+
+DO NOT MODIFY. AUTOGENERATED FILE.
+This file is generated using the logic from <root>/src/scripts/genEventPipe.py
+
+******************************************************************/
+"""
+
+stdprolog_cmake = """#
+#
+#******************************************************************
+
+#DO NOT MODIFY. AUTOGENERATED FILE.
+#This file is generated using the logic from <root>/src/scripts/genEventPipe.py
+
+#******************************************************************
+"""
+
+
+def generateClrEventPipeWriteEventsImpl(
+ providerName, eventNodes, allTemplates, exclusionListFile):
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ WriteEventImpl = []
+
+ # EventPipeEvent declaration
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ WriteEventImpl.append(
+ "EventPipeEvent *EventPipeEvent" +
+ eventName +
+ " = nullptr;\n")
+
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ templateName = eventNode.getAttribute('template')
+
+ # generate EventPipeEventEnabled function
+ eventEnabledImpl = """bool EventPipeEventEnabled%s()
+{
+ return EventPipeEvent%s->IsEnabled();
+}
+
+""" % (eventName, eventName)
+ WriteEventImpl.append(eventEnabledImpl)
+
+ # generate EventPipeWriteEvent function
+ fnptype = []
+ linefnptype = []
+ fnptype.append("extern \"C\" ULONG EventPipeWriteEvent")
+ fnptype.append(eventName)
+ fnptype.append("(\n")
+
+ if templateName:
+ template = allTemplates[templateName]
+ else:
+ template = None
+
+ if template:
+ fnSig = template.signature
+ for paramName in fnSig.paramlist:
+ fnparam = fnSig.getParam(paramName)
+ wintypeName = fnparam.winType
+ typewName = palDataTypeMapping[wintypeName]
+ winCount = fnparam.count
+ countw = palDataTypeMapping[winCount]
+
+ if paramName in template.structs:
+ linefnptype.append(
+ "%sint %s_ElementSize,\n" %
+ (lindent, paramName))
+
+ linefnptype.append(lindent)
+ linefnptype.append(typewName)
+ if countw != " ":
+ linefnptype.append(countw)
+
+ linefnptype.append(" ")
+ linefnptype.append(fnparam.name)
+ linefnptype.append(",\n")
+
+ if len(linefnptype) > 0:
+ del linefnptype[-1]
+
+ fnptype.extend(linefnptype)
+ fnptype.append(")\n{\n")
+ checking = """ if (!EventPipeEventEnabled%s())
+ return ERROR_SUCCESS;
+""" % (eventName)
+
+ fnptype.append(checking)
+
+ WriteEventImpl.extend(fnptype)
+
+ if template:
+ body = generateWriteEventBody(template, providerName, eventName)
+ WriteEventImpl.append(body)
+ else:
+ WriteEventImpl.append(
+ " EventPipe::WriteEvent(*EventPipeEvent" +
+ eventName +
+ ", nullptr, 0);\n")
+
+ WriteEventImpl.append("\n return ERROR_SUCCESS;\n}\n\n")
+
+ # EventPipeProvider and EventPipeEvent initialization
+ WriteEventImpl.append(
+ "extern \"C\" void Init" +
+ providerPrettyName +
+ "()\n{\n")
+ WriteEventImpl.append(
+ " EventPipeProvider" +
+ providerPrettyName +
+ " = new EventPipeProvider(" +
+ providerPrettyName +
+ "GUID);\n")
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ templateName = eventNode.getAttribute('template')
+ eventKeywords = eventNode.getAttribute('keywords')
+ eventKeywordsMask = generateEventKeywords(eventKeywords)
+ eventValue = eventNode.getAttribute('value')
+ eventVersion = eventNode.getAttribute('version')
+ eventLevel = eventNode.getAttribute('level')
+ eventLevel = eventLevel.replace("win:", "EventPipeEventLevel::")
+ exclusionInfo = parseExclusionList(exclusionListFile)
+ taskName = eventNode.getAttribute('task')
+ noStack = getStackWalkBit(
+ providerName,
+ taskName,
+ eventName,
+ exclusionInfo.nostack)
+
+ initEvent = """ EventPipeEvent%s = EventPipeProvider%s->AddEvent(%s,%s,%s,%s,%d);
+""" % (eventName, providerPrettyName, eventKeywordsMask, eventValue, eventVersion, eventLevel, int(noStack))
+
+ WriteEventImpl.append(initEvent)
+ WriteEventImpl.append("}")
+
+ return ''.join(WriteEventImpl)
+
+
+def generateWriteEventBody(template, providerName, eventName):
+ header = """
+ char stackBuffer[%s];
+ char *buffer = stackBuffer;
+ unsigned int offset = 0;
+ unsigned int size = %s;
+ bool fixedBuffer = true;
+
+ bool success = true;
+""" % (template.estimated_size, template.estimated_size)
+
+ fnSig = template.signature
+ pack_list = []
+ for paramName in fnSig.paramlist:
+ parameter = fnSig.getParam(paramName)
+
+ if paramName in template.structs:
+ size = "(int)%s_ElementSize * (int)%s" % (
+ paramName, parameter.prop)
+ if template.name in specialCaseSizes and paramName in specialCaseSizes[template.name]:
+ size = "(int)(%s)" % specialCaseSizes[template.name][paramName]
+ pack_list.append(
+ " success &= WriteToBuffer((const BYTE *)%s, %s, buffer, offset, size, fixedBuffer);" %
+ (paramName, size))
+ elif paramName in template.arrays:
+ size = "sizeof(%s) * (int)%s" % (
+ lttngDataTypeMapping[parameter.winType],
+ parameter.prop)
+ if template.name in specialCaseSizes and paramName in specialCaseSizes[template.name]:
+ size = "(int)(%s)" % specialCaseSizes[template.name][paramName]
+ pack_list.append(
+ " success &= WriteToBuffer((const BYTE *)%s, %s, buffer, offset, size, fixedBuffer);" %
+ (paramName, size))
+ elif parameter.winType == "win:GUID":
+ pack_list.append(
+ " success &= WriteToBuffer(*%s, buffer, offset, size, fixedBuffer);" %
+ (parameter.name,))
+ else:
+ pack_list.append(
+ " success &= WriteToBuffer(%s, buffer, offset, size, fixedBuffer);" %
+ (parameter.name,))
+
+ code = "\n".join(pack_list) + "\n\n"
+
+ checking = """ if (!success)
+ {
+ if (!fixedBuffer)
+ delete[] buffer;
+ return ERROR_WRITE_FAULT;
+ }\n\n"""
+
+ body = " EventPipe::WriteEvent(*EventPipeEvent" + \
+ eventName + ", (BYTE *)buffer, size);\n"
+
+ footer = """
+ if (!fixedBuffer)
+ delete[] buffer;
+"""
+
+ return header + code + checking + body + footer
+
+providerGUIDMap = {}
+providerGUIDMap[
+ "{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}"] = "{0xe13c0d23,0xccbc,0x4e12,{0x93,0x1b,0xd9,0xcc,0x2e,0xee,0x27,0xe4}}"
+providerGUIDMap[
+ "{A669021C-C450-4609-A035-5AF59AF4DF18}"] = "{0xA669021C,0xC450,0x4609,{0xA0,0x35,0x5A,0xF5,0x9A,0xF4,0xDF,0x18}}"
+providerGUIDMap[
+ "{CC2BCBBA-16B6-4cf3-8990-D74C2E8AF500}"] = "{0xCC2BCBBA,0x16B6,0x4cf3,{0x89,0x90,0xD7,0x4C,0x2E,0x8A,0xF5,0x00}}"
+providerGUIDMap[
+ "{763FD754-7086-4dfe-95EB-C01A46FAF4CA}"] = "{0x763FD754,0x7086,0x4dfe,{0x95,0xEB,0xC0,0x1A,0x46,0xFA,0xF4,0xCA}}"
+
+
+def generateGUID(tmpGUID):
+ return providerGUIDMap[tmpGUID]
+
+keywordMap = {}
+
+
+def generateEventKeywords(eventKeywords):
+ mask = 0
+ # split keywords if there are multiple
+ allKeywords = eventKeywords.split()
+
+ for singleKeyword in allKeywords:
+ mask = mask | keywordMap[singleKeyword]
+
+ return mask
+
+
+def generateEventPipeCmakeFile(etwmanifest, eventpipe_directory):
+ tree = DOM.parse(etwmanifest)
+
+ with open(eventpipe_directory + "CMakeLists.txt", 'w') as topCmake:
+ topCmake.write(stdprolog_cmake + "\n")
+ topCmake.write("""cmake_minimum_required(VERSION 2.8.12.2)
+
+ project(eventpipe)
+
+ set(CMAKE_INCLUDE_CURRENT_DIR ON)
+ include_directories(${CLR_DIR}/src/vm)
+
+ add_library(eventpipe
+ STATIC\n""")
+
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerName = providerNode.getAttribute('name')
+ providerName = providerName.replace("Windows-", '')
+ providerName = providerName.replace("Microsoft-", '')
+
+ providerName_File = providerName.replace('-', '')
+ providerName_File = providerName_File.lower()
+
+ topCmake.write(' "%s.cpp"\n' % (providerName_File))
+ topCmake.write(' "eventpipehelpers.cpp"\n')
+ topCmake.write(""" )
+
+ # Install the static eventpipe library
+ install(TARGETS eventpipe DESTINATION lib)
+ """)
+
+ topCmake.close()
+
+
+def generateEventPipeHelperFile(etwmanifest, eventpipe_directory):
+ with open(eventpipe_directory + "eventpipehelpers.cpp", 'w') as helper:
+ helper.write(stdprolog)
+ helper.write("""
+#include "stdlib.h"
+
+bool ResizeBuffer(char *&buffer, unsigned int& size, unsigned int currLen, unsigned int newSize, bool &fixedBuffer)
+{
+ newSize *= 1.5;
+ _ASSERTE(newSize > size); // check for overflow
+
+ if (newSize < 32)
+ newSize = 32;
+
+ char *newBuffer = new char[newSize];
+
+ memcpy(newBuffer, buffer, currLen);
+
+ if (!fixedBuffer)
+ delete[] buffer;
+
+ buffer = newBuffer;
+ size = newSize;
+ fixedBuffer = false;
+
+ return true;
+}
+
+bool WriteToBuffer(const BYTE *src, unsigned int len, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer)
+{
+ if(!src) return true;
+ if (offset + len > size)
+ {
+ if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer))
+ return false;
+ }
+
+ memcpy(buffer + offset, src, len);
+ offset += len;
+ return true;
+}
+
+bool WriteToBuffer(PCWSTR str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer)
+{
+ if(!str) return true;
+ unsigned int byteCount = (PAL_wcslen(str) + 1) * sizeof(*str);
+
+ if (offset + byteCount > size)
+ {
+ if (!ResizeBuffer(buffer, size, offset, size + byteCount, fixedBuffer))
+ return false;
+ }
+
+ memcpy(buffer + offset, str, byteCount);
+ offset += byteCount;
+ return true;
+}
+
+bool WriteToBuffer(const char *str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer)
+{
+ if(!str) return true;
+ unsigned int len = strlen(str) + 1;
+ if (offset + len > size)
+ {
+ if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer))
+ return false;
+ }
+
+ memcpy(buffer + offset, str, len);
+ offset += len;
+ return true;
+}
+
+""")
+
+ tree = DOM.parse(etwmanifest)
+
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerName = providerNode.getAttribute('name')
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ helper.write(
+ "extern \"C\" void Init" +
+ providerPrettyName +
+ "();\n\n")
+
+ helper.write("extern \"C\" void InitProvidersAndEvents()\n{\n")
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerName = providerNode.getAttribute('name')
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ helper.write(" Init" + providerPrettyName + "();\n")
+ helper.write("}")
+
+ helper.close()
+
+
+def generateEventPipeImplFiles(
+ etwmanifest, eventpipe_directory, exclusionListFile):
+ tree = DOM.parse(etwmanifest)
+ coreclrRoot = os.getcwd()
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerGUID = providerNode.getAttribute('guid')
+ providerGUID = generateGUID(providerGUID)
+ providerName = providerNode.getAttribute('name')
+
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerName_File = providerPrettyName.replace('-', '')
+ providerName_File = providerName_File.lower()
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ eventpipefile = eventpipe_directory + providerName_File + ".cpp"
+ eventpipeImpl = open(eventpipefile, 'w')
+ eventpipeImpl.write(stdprolog)
+
+ header = """
+#include \"%s/src/vm/common.h\"
+#include \"%s/src/vm/eventpipeprovider.h\"
+#include \"%s/src/vm/eventpipeevent.h\"
+#include \"%s/src/vm/eventpipe.h\"
+
+bool ResizeBuffer(char *&buffer, unsigned int& size, unsigned int currLen, unsigned int newSize, bool &fixedBuffer);
+bool WriteToBuffer(PCWSTR str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer);
+bool WriteToBuffer(const char *str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer);
+bool WriteToBuffer(const BYTE *src, unsigned int len, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer);
+
+template <typename T>
+bool WriteToBuffer(const T &value, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer)
+{
+ if (sizeof(T) + offset > size)
+ {
+ if (!ResizeBuffer(buffer, size, offset, size + sizeof(T), fixedBuffer))
+ return false;
+ }
+
+ *(T *)(buffer + offset) = value;
+ offset += sizeof(T);
+ return true;
+}
+
+""" % (coreclrRoot, coreclrRoot, coreclrRoot, coreclrRoot)
+
+ eventpipeImpl.write(header)
+ eventpipeImpl.write(
+ "GUID const " +
+ providerPrettyName +
+ "GUID = " +
+ providerGUID +
+ ";\n")
+ eventpipeImpl.write(
+ "EventPipeProvider *EventPipeProvider" +
+ providerPrettyName +
+ " = nullptr;\n")
+ templateNodes = providerNode.getElementsByTagName('template')
+ allTemplates = parseTemplateNodes(templateNodes)
+ eventNodes = providerNode.getElementsByTagName('event')
+ eventpipeImpl.write(
+ generateClrEventPipeWriteEventsImpl(
+ providerName,
+ eventNodes,
+ allTemplates,
+ exclusionListFile) + "\n")
+ eventpipeImpl.close()
+
+
+def generateEventPipeFiles(
+ etwmanifest, eventpipe_directory, exclusionListFile):
+ eventpipe_directory = eventpipe_directory + "/"
+ tree = DOM.parse(etwmanifest)
+
+ if not os.path.exists(eventpipe_directory):
+ os.makedirs(eventpipe_directory)
+
+ # generate Cmake file
+ generateEventPipeCmakeFile(etwmanifest, eventpipe_directory)
+
+ # generate helper file
+ generateEventPipeHelperFile(etwmanifest, eventpipe_directory)
+
+ # generate all keywords
+ for keywordNode in tree.getElementsByTagName('keyword'):
+ keywordName = keywordNode.getAttribute('name')
+ keywordMask = keywordNode.getAttribute('mask')
+ keywordMap[keywordName] = int(keywordMask, 0)
+
+ # generate .cpp file for each provider
+ generateEventPipeImplFiles(
+ etwmanifest,
+ eventpipe_directory,
+ exclusionListFile)
+
+import argparse
+import sys
+
+
+def main(argv):
+
+ # parse the command line
+ parser = argparse.ArgumentParser(
+ description="Generates the Code required to instrument eventpipe logging mechanism")
+
+ required = parser.add_argument_group('required arguments')
+ required.add_argument('--man', type=str, required=True,
+ help='full path to manifest containig the description of events')
+ required.add_argument('--intermediate', type=str, required=True,
+ help='full path to eventprovider intermediate directory')
+ required.add_argument('--exc', type=str, required=True,
+ help='full path to exclusion list')
+ args, unknown = parser.parse_known_args(argv)
+ if unknown:
+ print('Unknown argument(s): ', ', '.join(unknown))
+ return const.UnknownArguments
+
+ sClrEtwAllMan = args.man
+ intermediate = args.intermediate
+ exclusionListFile = args.exc
+
+ generateEventPipeFiles(sClrEtwAllMan, intermediate, exclusionListFile)
+
+if __name__ == '__main__':
+ return_code = main(sys.argv[1:])
+ sys.exit(return_code)
#******************************************************************
"""
-lindent = " ";
+lindent = " ";
palDataTypeMapping ={
#constructed types
"win:null" :" ",
#generate EventEnabled
clrallEvents.append("inline BOOL EventEnabled")
clrallEvents.append(eventName)
- clrallEvents.append("() {return XplatEventLogger::IsEventLoggingEnabled() && EventXplatEnabled")
- clrallEvents.append(eventName+"();}\n\n")
+ clrallEvents.append("() {return ")
+ clrallEvents.append("EventPipeEventEnabled" + eventName + "() || ")
+ clrallEvents.append("(XplatEventLogger::IsEventLoggingEnabled() && EventXplatEnabled")
+ clrallEvents.append(eventName+"());}\n\n")
#generate FireEtw functions
fnptype = []
fnbody = []
fnptype.append("inline ULONG FireEtw")
fnptype.append(eventName)
fnptype.append("(\n")
- fnbody.append(lindent)
- fnbody.append("if (!EventEnabled")
- fnbody.append(eventName)
- fnbody.append("()) {return ERROR_SUCCESS;}\n")
+
line = []
fnptypeline = []
fnptype.extend(fnptypeline)
fnptype.append("\n)\n{\n")
fnbody.append(lindent)
- fnbody.append("return FireEtXplat")
+ fnbody.append("ULONG status = EventPipeWriteEvent" + eventName + "(" + ''.join(line) + ");\n")
+ fnbody.append(lindent)
+ fnbody.append("if(XplatEventLogger::IsEventLoggingEnabled())\n")
+ fnbody.append(lindent)
+ fnbody.append("{\n")
+ fnbody.append(lindent)
+ fnbody.append(lindent)
+ fnbody.append("status &= FireEtXplat")
fnbody.append(eventName)
fnbody.append("(")
fnbody.extend(line)
fnbody.append(");\n")
+ fnbody.append(lindent)
+ fnbody.append("}\n")
+ fnbody.append(lindent)
+ fnbody.append("return status;\n")
fnbody.append("}\n\n")
clrallEvents.extend(fnptype)
return ''.join(clrallEvents)
+def generateClrEventPipeWriteEvents(eventNodes, allTemplates):
+ clrallEvents = []
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ templateName = eventNode.getAttribute('template')
+
+ #generate EventPipeEventEnabled and EventPipeWriteEvent functions
+ eventenabled = []
+ writeevent = []
+ fnptypeline = []
+
+ eventenabled.append("extern \"C\" bool EventPipeEventEnabled")
+ eventenabled.append(eventName)
+ eventenabled.append("();\n")
+
+ writeevent.append("extern \"C\" ULONG EventPipeWriteEvent")
+ writeevent.append(eventName)
+ writeevent.append("(\n")
+
+ if templateName:
+ template = allTemplates[templateName]
+ fnSig = template.signature
+
+ for params in fnSig.paramlist:
+ fnparam = fnSig.getParam(params)
+ wintypeName = fnparam.winType
+ typewName = palDataTypeMapping[wintypeName]
+ winCount = fnparam.count
+ countw = palDataTypeMapping[winCount]
+
+ if params in template.structs:
+ fnptypeline.append("%sint %s_ElementSize,\n" % (lindent, params))
+
+ fnptypeline.append(lindent)
+ fnptypeline.append(typewName)
+ fnptypeline.append(countw)
+ fnptypeline.append(" ")
+ fnptypeline.append(fnparam.name)
+ fnptypeline.append(",\n")
+
+ #remove trailing commas
+ if len(fnptypeline) > 0:
+ del fnptypeline[-1]
+
+ writeevent.extend(fnptypeline)
+ writeevent.append("\n);\n")
+ clrallEvents.extend(eventenabled)
+ clrallEvents.extend(writeevent)
+
+ return ''.join(clrallEvents)
+
#generates the dummy header file which is used by the VM as entry point to the logging Functions
def generateclrEtwDummy(eventNodes,allTemplates):
clretmEvents = []
clrallevents = incDir + "/clretwallmain.h"
clrxplatevents = incDir + "/clrxplatevents.h"
+ clreventpipewriteevents = incDir + "/clreventpipewriteevents.h"
Clrallevents = open(clrallevents,'w')
Clrxplatevents = open(clrxplatevents,'w')
+ Clreventpipewriteevents = open(clreventpipewriteevents,'w')
Clrallevents.write(stdprolog + "\n")
Clrxplatevents.write(stdprolog + "\n")
+ Clreventpipewriteevents.write(stdprolog + "\n")
- Clrallevents.write("\n#include \"clrxplatevents.h\"\n\n")
-
+ Clrallevents.write("\n#include \"clrxplatevents.h\"\n")
+ Clrallevents.write("#include \"clreventpipewriteevents.h\"\n\n")
+
for providerNode in tree.getElementsByTagName('provider'):
templateNodes = providerNode.getElementsByTagName('template')
allTemplates = parseTemplateNodes(templateNodes)
#pal: create clrallevents.h
Clrxplatevents.write(generateClrXplatEvents(eventNodes, allTemplates) + "\n")
+ #eventpipe: create clreventpipewriteevents.h
+ Clreventpipewriteevents.write(generateClrEventPipeWriteEvents(eventNodes, allTemplates) + "\n")
+
Clrxplatevents.close()
Clrallevents.close()
+ Clreventpipewriteevents.close()
class EventExclusions:
def __init__(self):
bool WriteToBuffer(const BYTE *src, int len, char *&buffer, int& offset, int& size, bool &fixedBuffer)
{
- if (offset + len)
+ if (!src) return true;
+ if (offset + len > size)
{
if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer))
return false;
bool WriteToBuffer(PCWSTR str, char *&buffer, int& offset, int& size, bool &fixedBuffer)
{
+ if (!str) return true;
int byteCount = (PAL_wcslen(str) + 1) * sizeof(*str);
- if (offset + byteCount)
+ if (offset + byteCount > size)
{
if (!ResizeBuffer(buffer, size, offset, size + byteCount, fixedBuffer))
return false;
bool WriteToBuffer(const char *str, char *&buffer, int& offset, int& size, bool &fixedBuffer)
{
+ if (!str) return true;
int len = strlen(str) + 1;
- if (offset + len)
+ if (offset + len > size)
{
if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer))
return false;
if __name__ == '__main__':
return_code = main(sys.argv[1:])
- sys.exit(return_code)
\ No newline at end of file
+ sys.exit(return_code)
// Reserves free memory within the range [pMinAddr..pMaxAddr] using
// ClrVirtualQuery to find free memory and ClrVirtualAlloc to reserve it.
//
-// This method only supports the flAllocationType of MEM_RESERVE
+// This method only supports the flAllocationType of MEM_RESERVE, and expects that the memory
+// is being reserved for the purpose of eventually storing executable code.
+//
// Callers also should set dwSize to a multiple of sysInfo.dwAllocationGranularity (64k).
// That way they can reserve a large region and commit smaller sized pages
// from that region until it fills up.
static unsigned countOfCalls = 0; // We log the number of tims we call this method
countOfCalls++; // increment the call counter
+ if (dwSize == 0)
+ {
+ return nullptr;
+ }
+
//
// First lets normalize the pMinAddr and pMaxAddr values
//
pMaxAddr = (BYTE *) TOP_MEMORY;
}
+ // If pMaxAddr is not greater than pMinAddr we can not make an allocation
+ if (pMaxAddr <= pMinAddr)
+ {
+ return nullptr;
+ }
+
// If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY
// then we can call ClrVirtualAlloc instead
if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY))
{
- return (BYTE*) ClrVirtualAlloc(NULL, dwSize, flAllocationType, flProtect);
+ return (BYTE*) ClrVirtualAlloc(nullptr, dwSize, flAllocationType, flProtect);
}
- // If pMaxAddr is not greater than pMinAddr we can not make an allocation
- if (dwSize == 0 || pMaxAddr <= pMinAddr)
+#ifdef FEATURE_PAL
+ pResult = (BYTE *)PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(pMinAddr, pMaxAddr, dwSize);
+ if (pResult != nullptr)
{
- return NULL;
+ return pResult;
}
+#endif // FEATURE_PAL
// We will do one scan from [pMinAddr .. pMaxAddr]
// First align the tryAddr up to next 64k base address.
eepolicy.cpp
eetoprofinterfaceimpl.cpp
eventpipe.cpp
+ eventpipeconfiguration.cpp
+ eventpipeevent.cpp
+ eventpipeeventinstance.cpp
+ eventpipefile.cpp
eventpipejsonfile.cpp
+ eventpipeprovider.cpp
eventstore.cpp
+ fastserializer.cpp
fcall.cpp
fieldmarshaler.cpp
finalizerthread.cpp
add_subdirectory(dac)
add_subdirectory(wks)
+
+if(CLR_CMAKE_PLATFORM_LINUX)
+ add_subdirectory($ENV{__IntermediatesDir}/Generated/eventpipe ${CMAKE_CURRENT_BINARY_DIR}/eventpipe)
+endif(CLR_CMAKE_PLATFORM_LINUX)
NESTED_END ThePreStub, _TEXT
// ------------------------------------------------------------------
+ NESTED_ENTRY ThePreStubCompactARM, _TEXT, NoHandler
+
+ // r12 - address of compact entry point + PC_REG_RELATIVE_OFFSET
+
+ PROLOG_WITH_TRANSITION_BLOCK
+
+ mov r0, r12
+
+ bl C_FUNC(PreStubGetMethodDescForCompactEntryPoint)
+
+ mov r12, r0 // pMethodDesc
+
+ EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+
+ b C_FUNC(ThePreStub)
+
+ NESTED_END ThePreStubCompactARM, _TEXT
+// ------------------------------------------------------------------
// This method does nothing. It's just a fixed function for the debugger to put a breakpoint on.
LEAF_ENTRY ThePreStubPatch, _TEXT
nop
IMPORT UMThunkStubRareDisableWorker
IMPORT UM2MDoADCallBack
IMPORT PreStubWorker
+ IMPORT PreStubGetMethodDescForCompactEntryPoint
IMPORT NDirectImportWorker
IMPORT ObjIsInstanceOfNoGC
IMPORT ArrayStoreCheck
NESTED_END
; ------------------------------------------------------------------
+
+ NESTED_ENTRY ThePreStubCompactARM
+
+ ; r12 - address of compact entry point + PC_REG_RELATIVE_OFFSET
+
+ PROLOG_WITH_TRANSITION_BLOCK
+
+ mov r0, r12
+
+ bl PreStubGetMethodDescForCompactEntryPoint
+
+ mov r12, r0 ; pMethodDesc
+
+ EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
+
+ b ThePreStub
+
+ NESTED_END
+
+; ------------------------------------------------------------------
; This method does nothing. It's just a fixed function for the debugger to put a breakpoint on.
LEAF_ENTRY ThePreStubPatch
nop
#define JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a jump instruction
#define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a back to back jump instruction
-//#define HAS_COMPACT_ENTRYPOINTS 1
+#define HAS_COMPACT_ENTRYPOINTS 1
#define HAS_NDIRECT_IMPORT_PRECODE 1
// this is the offset by which it should be decremented to arrive at the callsite.
#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
+// Max offset for unconditional thumb branch
+#define MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB 2048
+
+// Offset of pc register
+#define PC_REG_RELATIVE_OFFSET 4
+
//=======================================================================
// IMPORTANT: This value is used to figure out how much to allocate
// for a fixed array of FieldMarshaler's. That means it must be at least
#endif // FEATURE_COMINTEROP
//------------------------------------------------------------------------
+inline void emitUnconditionalBranchThumb(LPBYTE pBuffer, int16_t offset)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ uint16_t *pInstr = (uint16_t *) pBuffer;
+
+ // offset from -2KB to +2KB
+ _ASSERTE (offset >= - MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB && offset < MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB);
+
+ if (offset >= 0)
+ {
+ offset = offset >> 1;
+ }
+ else
+ {
+ offset = ((MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB + offset) >> 1) | 0x400;
+ }
+
+ *pInstr = 0xE000 | offset;
+}
+
+//------------------------------------------------------------------------
+inline int16_t decodeUnconditionalBranchThumb(LPBYTE pBuffer)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ uint16_t *pInstr = (uint16_t *) pBuffer;
+
+ int16_t offset = (~0xE000) & (*pInstr);
+
+ if ((offset & 0x400) == 0)
+ {
+ offset = offset << 1;
+ }
+ else
+ {
+ offset = (~0x400) & offset;
+ offset = (offset << 1) - MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB;
+ }
+
+ // offset from -2KB to +2KB
+ _ASSERTE (offset >= - MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB && offset < MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB);
+
+ return offset;
+}
+
+//------------------------------------------------------------------------
inline void emitJump(LPBYTE pBuffer, LPVOID target)
{
LIMITED_METHOD_CONTRACT;
{
PTR_WORD pInstr = dac_cast<PTR_WORD>(PCODEToPINSTR(pCode));
+#ifdef HAS_COMPACT_ENTRYPOINTS
+ if (MethodDescChunk::GetMethodDescFromCompactEntryPoint(pCode, TRUE) != NULL)
+ {
+ return TRUE;
+ }
+#endif // HAS_COMPACT_ENTRYPOINTS
+
// FixupPrecode
if (pInstr[0] == 0x46fc && // // mov r12, pc
pInstr[1] == 0xf8df &&
// x13 : incremented by 8
// x14 : incremented by 8
// x15 : trashed
+// x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
//
WRITE_BARRIER_ENTRY JIT_ByRefWriteBarrier
// x12 : trashed
// x14 : incremented by 8
// x15 : trashed
+// x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
//
WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier
PREPARE_EXTERNAL_VAR g_lowest_address, x12
// x12 : trashed
// x14 : incremented by 8
// x15 : trashed
+// x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
//
WRITE_BARRIER_ENTRY JIT_WriteBarrier
dmb ST
ldp x12, x13, [sp],#16
#endif
+#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+ // Update the write watch table if necessary
+ PREPARE_EXTERNAL_VAR g_sw_ww_enabled_for_gc_heap, x12
+ ldrb w12, [x12]
+ cbz x12, LOCAL_LABEL(CheckCardTable)
+ PREPARE_EXTERNAL_VAR g_sw_ww_table, x12
+ ldr x12, [x12]
+ add x12, x12, x14, lsr #0xc // SoftwareWriteWatch::AddressToTableByteIndexShift
+ ldrb w17, [x12]
+ cbnz x17, LOCAL_LABEL(CheckCardTable)
+ mov w17, #0xFF
+ strb w17, [x12]
+#endif
+
+LOCAL_LABEL(CheckCardTable):
// Branch to Exit if the reference is not in the Gen0 heap
//
PREPARE_EXTERNAL_VAR g_ephemeral_low, x12
LOCAL_LABEL(UpdateCardTable):
mov x12, 0xFF
strb w12, [x15]
+
+#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
+ // Check if we need to update the card table
+ PREPARE_EXTERNAL_VAR g_card_bundle_table, x12
+ ldr x12, [x12]
+ add x15, x12, x14, lsr #21
+ ldrb w12, [x15]
+ cmp x12, 0xFF
+ beq LOCAL_LABEL(Exit)
+
+LOCAL_LABEL(UpdateCardBundle):
+ mov x12, 0xFF
+ strb w12, [x15]
+#endif
+
LOCAL_LABEL(Exit):
add x14, x14, 8
ret lr
return;
}
+#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+void SwitchToWriteWatchBarrier(bool isRuntimeSuspended)
+{
+ return;
+}
+
+void SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended)
+{
+ return;
+}
+#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+
+
#ifdef DACCESS_COMPILE
BOOL GetAnyThunkTarget (T_CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc)
{
return pAssembly;
}
-FCIMPL6(Object*, AssemblyNative::LoadImage, U1Array* PEByteArrayUNSAFE,
- U1Array* SymByteArrayUNSAFE, Object* securityUNSAFE,
- StackCrawlMark* stackMark, CLR_BOOL fForIntrospection, SecurityContextSource securityContextSource)
-{
- FCALL_CONTRACT;
-
- struct _gc
- {
- U1ARRAYREF PEByteArray;
- U1ARRAYREF SymByteArray;
- OBJECTREF security;
- OBJECTREF Throwable;
- OBJECTREF refRetVal;
- } gc;
-
- gc.PEByteArray = (U1ARRAYREF) PEByteArrayUNSAFE;
- gc.SymByteArray = (U1ARRAYREF) SymByteArrayUNSAFE;
- gc.security = (OBJECTREF) securityUNSAFE;
- gc.Throwable = NULL;
- gc.refRetVal = NULL;
-
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
-
-
- if (gc.PEByteArray == NULL)
- COMPlusThrow(kArgumentNullException, W("ArgumentNull_Array"));
-
- NewArrayHolder<BYTE> pbSyms;
- DWORD cbSyms = 0;
-
-#ifdef DEBUGGING_SUPPORTED
- // If we were given symbols, save a copy of them.
- // the debugger, load them now).
- if (gc.SymByteArray != NULL)
- {
- Security::CopyByteArrayToEncoding(&gc.SymByteArray,
- &pbSyms, &cbSyms);
-
- }
-#endif // DEBUGGING_SUPPORTED
-
- Assembly* pAssembly = NULL;
- // Pin byte array for loading
- {
- Wrapper<OBJECTHANDLE, DoNothing, DestroyPinningHandle> handle(
- GetAppDomain()->CreatePinningHandle(gc.PEByteArray));
-
- const BYTE *pbImage = gc.PEByteArray->GetDirectConstPointerToNonObjectElements();
- DWORD cbImage = gc.PEByteArray->GetNumComponents();
- pAssembly = LoadFromBuffer(fForIntrospection, pbImage, cbImage, pbSyms, cbSyms, stackMark, OBJECTREFToObject(gc.security), securityContextSource);
- }
-
-
- if (pAssembly != NULL)
- gc.refRetVal = pAssembly->GetExposedObject();
-
- HELPER_METHOD_FRAME_END();
-
- return OBJECTREFToObject(gc.refRetVal);
-}
-FCIMPLEND
-
-FCIMPL2(Object*, AssemblyNative::LoadFile, StringObject* pathUNSAFE, Object* securityUNSAFE)
-{
- FCALL_CONTRACT;
-
- struct _gc {
- OBJECTREF refRetVal;
- OBJECTREF refSecurity;
- STRINGREF strPath;
- } gc;
-
- gc.refRetVal = NULL;
- gc.refSecurity = ObjectToOBJECTREF(securityUNSAFE);
- gc.strPath = ObjectToSTRINGREF(pathUNSAFE);
-
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
-
- if(CorHost2::IsLoadFromBlocked())
- COMPlusThrow(kFileLoadException, FUSION_E_LOADFROM_BLOCKED);
-
- if (pathUNSAFE == NULL)
- COMPlusThrow(kArgumentNullException, W("ArgumentNull_Path"));
-
- StackSString path;
- gc.strPath->GetSString(path);
-
- Assembly *pAssembly = AssemblySpec::LoadAssembly(path);
-
- LOG((LF_CLASSLOADER,
- LL_INFO100,
- "\tLoaded assembly from a file\n"));
-
-
- if (pAssembly != NULL)
- gc.refRetVal = (ASSEMBLYREF) pAssembly->GetExposedObject();
-
- HELPER_METHOD_FRAME_END();
-
- return OBJECTREFToObject(gc.refRetVal);
-}
-FCIMPLEND
-
-
/* static */
Assembly* AssemblyNative::LoadFromPEImage(ICLRPrivBinder* pBinderContext, PEImage *pILImage, PEImage *pNIImage)
{
static
void QCALLTYPE GetExecutingAssembly(QCall::StackCrawlMarkHandle stackMark, QCall::ObjectHandleOnStack retAssembly);
- static FCDECL2(Object*, LoadFile, StringObject* pathUNSAFE,
- Object* securityUNSAFE);
- static FCDECL6(Object*, LoadImage, U1Array* PEByteArrayUNSAFE, U1Array* SymByteArrayUNSAFE, Object* securityUNSAFE, StackCrawlMark* stackMark, CLR_BOOL fForIntrospection, SecurityContextSource securityContextSource);
-
static FCDECL10(Object*, Load, AssemblyNameBaseObject* assemblyNameUNSAFE,
StringObject* codeBaseUNSAFE,
Object* securityUNSAFE,
CLR_BOOL fSuppressSecurityChecks,
INT_PTR ptrLoadContextBinder);
- static FCDECL1(FC_BOOL_RET, IsFrameworkAssembly, AssemblyNameBaseObject* refAssemblyNameUNSAFE);
- static FCDECL1(FC_BOOL_RET, IsNewPortableAssembly, AssemblyNameBaseObject* refAssemblyNameUNSAFE);
-
//
// instance FCALLs
//
InitThreadManager();
STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "Returned successfully from InitThreadManager");
+#ifdef FEATURE_PERFTRACING
+ // Initialize the event pipe.
+ EventPipe::Initialize();
+#endif // FEATURE_PERFTRACING
+
#ifdef FEATURE_EVENT_TRACE
// Initialize event tracing early so we can trace CLR startup time events.
InitializeEventTracing();
#endif
#ifdef FEATURE_PERFTRACING
- // Initialize the event pipe and start it if requested.
- EventPipe::Initialize();
+ // Start the event pipe if requested.
EventPipe::EnableOnStartup();
#endif // FEATURE_PERFTRACING
return GetEEFuncEntryPoint(ThePreStub);
}
+#if defined(HAS_COMPACT_ENTRYPOINTS) && defined(_TARGET_ARM_)
+
+EXTERN_C void STDCALL ThePreStubCompactARM();
+
+inline PCODE GetPreStubCompactARMEntryPoint()
+{
+ return GetEEFuncEntryPoint(ThePreStubCompactARM);
+}
+
+#endif // defined(HAS_COMPACT_ENTRYPOINTS) && defined(_TARGET_ARM_)
+
PCODE TheUMThunkPreStub();
PCODE TheVarargNDirectStub(BOOL hasRetBuffArg);
QCFuncElement("GetVersion", AssemblyNative::GetVersion)
FCFuncElement("FCallIsDynamic", AssemblyNative::IsDynamic)
FCFuncElement("_nLoad", AssemblyNative::Load)
- FCFuncElement("nLoadImage", AssemblyNative::LoadImage)
QCFuncElement("GetType", AssemblyNative::GetType)
QCFuncElement("GetManifestResourceInfo", AssemblyNative::GetManifestResourceInfo)
QCFuncElement("GetModules", AssemblyNative::GetModules)
HandleExitProcessHelper(action, 0, sca);
}
+StackWalkAction LogCallstackForLogCallback(
+ CrawlFrame *pCF, //
+ VOID* pData // Caller's private data
+)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ SmallStackSString *pWordAt = ((SmallStackSString*)pData);
+
+ MethodDesc *pMD = pCF->GetFunction();
+ _ASSERTE(pMD != NULL);
+
+ StackSString str;
+ str = *pWordAt;
+
+ TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature);
+ PrintToStdErrW(str.GetUnicode());
+ PrintToStdErrA("\n");
+
+ return SWA_CONTINUE;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// A worker to save managed stack trace.
+//
+// Arguments:
+// reporter - EventReporter object for EventLog
+//
+// Return Value:
+// None
+//
+inline void LogCallstackForLogWorker()
+{
+ Thread* pThread = GetThread();
+ _ASSERTE (pThread);
+
+ SmallStackSString WordAt;
+
+ if (!WordAt.LoadResource(CCompRC::Optional, IDS_ER_WORDAT))
+ {
+ WordAt.Set(W(" at"));
+ }
+ else
+ {
+ WordAt.Insert(WordAt.Begin(), W(" "));
+ }
+ WordAt += W(" ");
+
+ pThread->StackWalkFrames(&LogCallstackForLogCallback, &WordAt, QUICKUNWIND | FUNCTIONSONLY);
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Generate an EventLog entry for unhandled exception.
+//
+// Arguments:
+// pExceptionInfo - Exception information
+//
+// Return Value:
+// None
+//
+inline void DoLogForFailFastException(LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo)
+{
+ WRAPPER_NO_CONTRACT;
+
+ Thread *pThread = GetThread();
+ EX_TRY
+ {
+ PrintToStdErrA("FailFast: ");
+ PrintToStdErrW((WCHAR*)pszMessage);
+ PrintToStdErrA("\n");
+
+ if (pThread)
+ {
+ PrintToStdErrA("\n");
+ LogCallstackForLogWorker();
+ }
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+}
+
//
// Log an error to the event log if possible, then throw up a dialog box.
//
_ASSERTE(pExceptionInfo != NULL);
+ // Log FailFast exception to StdErr
+ if (exitCode == (UINT)COR_E_FAILFAST)
+ {
+ DoLogForFailFastException(pszMessage, pExceptionInfo);
+ }
+
if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
{
// Fire an ETW FailFast event
#include "common.h"
#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeevent.h"
+#include "eventpipefile.h"
+#include "eventpipeprovider.h"
#include "eventpipejsonfile.h"
#include "sampleprofiler.h"
-CrstStatic EventPipe::s_initCrst;
+#ifdef FEATURE_PAL
+#include "pal.h"
+#endif // FEATURE_PAL
+
+#ifdef FEATURE_PERFTRACING
+
+CrstStatic EventPipe::s_configCrst;
bool EventPipe::s_tracingInitialized = false;
-bool EventPipe::s_tracingEnabled = false;
+EventPipeConfiguration* EventPipe::s_pConfig = NULL;
+EventPipeFile* EventPipe::s_pFile = NULL;
EventPipeJsonFile* EventPipe::s_pJsonFile = NULL;
+#ifdef FEATURE_PAL
+// This function is auto-generated from /src/scripts/genEventPipe.py
+extern "C" void InitProvidersAndEvents();
+#endif
+
void EventPipe::Initialize()
{
STANDARD_VM_CONTRACT;
- s_tracingInitialized = s_initCrst.InitNoThrow(
+ s_tracingInitialized = s_configCrst.InitNoThrow(
CrstEventPipe,
- (CrstFlags)(CRST_TAKEN_DURING_SHUTDOWN));
+ (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
+
+ s_pConfig = new EventPipeConfiguration();
+ s_pConfig->Initialize();
+
+#ifdef FEATURE_PAL
+ // This calls into auto-generated code to initialize the runtime providers
+ // and events so that the EventPipe configuration lock isn't taken at runtime
+ InitProvidersAndEvents();
+#endif
}
void EventPipe::EnableOnStartup()
return;
}
- // Take the lock and enable tracing.
- CrstHolder _crst(&s_initCrst);
- s_tracingEnabled = true;
+ // Take the lock before enabling tracing.
+ CrstHolder _crst(GetLock());
+
+ // Create the event pipe file.
+ SString eventPipeFileOutputPath;
+ eventPipeFileOutputPath.Printf("Process-%d.netperf", GetCurrentProcessId());
+ s_pFile = new EventPipeFile(eventPipeFileOutputPath);
+
if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) == 2)
{
// File placed in current working directory.
s_pJsonFile = new EventPipeJsonFile(outputFilePath);
}
+ // Enable tracing.
+ s_pConfig->Enable();
+
+ // Enable the sample profiler
SampleProfiler::Enable();
+
+ // TODO: Iterate through the set of providers, enable them as appropriate.
+ // This in-turn will iterate through all of the events and set their isEnabled bits.
}
void EventPipe::Disable()
}
CONTRACTL_END;
- CrstHolder _crst(&s_initCrst);
- s_tracingEnabled = false;
+ // Don't block GC during clean-up.
+ GCX_PREEMP();
+
+ // Take the lock before disabling tracing.
+ CrstHolder _crst(GetLock());
+
+ // Disable the profiler.
SampleProfiler::Disable();
+ // Disable tracing.
+ s_pConfig->Disable();
+
if(s_pJsonFile != NULL)
{
delete(s_pJsonFile);
s_pJsonFile = NULL;
}
-}
-bool EventPipe::EventEnabled(GUID& providerID, INT64 keyword)
-{
- CONTRACTL
+ if(s_pFile != NULL)
{
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
+ delete(s_pFile);
+ s_pFile = NULL;
}
- CONTRACTL_END;
- // TODO: Implement filtering.
- return false;
+ if(s_pConfig != NULL)
+ {
+ delete(s_pConfig);
+ s_pConfig = NULL;
+ }
}
-void EventPipe::WriteEvent(GUID& providerID, INT64 eventID, BYTE *pData, size_t length, bool sampleStack)
+void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length)
{
CONTRACTL
{
}
CONTRACTL_END;
- StackContents stackContents;
- bool stackWalkSucceeded;
-
- if(sampleStack)
+ // Exit early if the event is not enabled.
+ if(!event.IsEnabled())
{
- stackWalkSucceeded = WalkManagedStackForCurrentThread(stackContents);
+ return;
}
- // TODO: Write the event.
+ DWORD threadID = GetCurrentThreadId();
+
+ // Create an instance of the event.
+ EventPipeEventInstance instance(
+ event,
+ threadID,
+ pData,
+ length);
+
+ // Write to the EventPipeFile.
+ _ASSERTE(s_pFile != NULL);
+ s_pFile->WriteEvent(instance);
+
+ // Write to the EventPipeJsonFile if it exists.
+ if(s_pJsonFile != NULL)
+ {
+ s_pJsonFile->WriteEvent(instance);
+ }
}
-void EventPipe::WriteSampleProfileEvent(Thread *pThread, StackContents &stackContents)
+void EventPipe::WriteSampleProfileEvent(SampleProfilerEventInstance &instance)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
- PRECONDITION(pThread != NULL);
}
CONTRACTL_END;
- EX_TRY
+ // Write to the EventPipeFile.
+ if(s_pFile != NULL)
{
- if(s_pJsonFile != NULL)
- {
- CommonEventFields eventFields;
- QueryPerformanceCounter(&eventFields.TimeStamp);
- eventFields.ThreadID = pThread->GetOSThreadId();
+ s_pFile->WriteEvent(instance);
+ }
- static SString message(W("THREAD_TIME"));
- s_pJsonFile->WriteEvent(eventFields, message, stackContents);
- }
+ // Write to the EventPipeJsonFile if it exists.
+ if(s_pJsonFile != NULL)
+ {
+ s_pJsonFile->WriteEvent(instance);
}
- EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
}
bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
CONTRACTL_END;
Thread *pThread = GetThread();
- _ASSERTE(pThread != NULL);
- return WalkManagedStackForThread(pThread, stackContents);
+ if(pThread != NULL)
+ {
+ return WalkManagedStackForThread(pThread, stackContents);
+ }
+
+ return false;
}
bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackContents)
// Continue the stack walk.
return SWA_CONTINUE;
}
+
+EventPipeConfiguration* EventPipe::GetConfiguration()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return s_pConfig;
+}
+
+CrstStatic* EventPipe::GetLock()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return &s_configCrst;
+}
+
+#endif // FEATURE_PERFTRACING
#ifndef __EVENTPIPE_H__
#define __EVENTPIPE_H__
-#include "common.h"
+#ifdef FEATURE_PERFTRACING
-class EventPipeJsonFile;
-
-// The data fields common to every event.
-struct CommonEventFields
-{
- // Timestamp generated by QueryPerformanceCounter.
- LARGE_INTEGER TimeStamp;
+#include "crst.h"
+#include "stackwalk.h"
- // Thread ID.
- DWORD ThreadID;
-};
+class EventPipeConfiguration;
+class EventPipeEvent;
+class EventPipeFile;
+class EventPipeJsonFile;
+class MethodDesc;
+class SampleProfilerEventInstance;
class StackContents
{
m_nextAvailableFrame++;
}
}
+
+ BYTE* GetPointer() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (BYTE*)m_stackFrames;
+ }
+
+ unsigned int GetSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_nextAvailableFrame * sizeof(UINT_PTR));
+ }
};
class EventPipe
{
+ // Declare friends.
+ friend class EventPipeConfiguration;
+ friend class EventPipeFile;
+ friend class EventPipeProvider;
+ friend class SampleProfiler;
+
public:
// Initialize the event pipe.
// Disable tracing via the event pipe.
static void Disable();
- // Determine whether or not the specified provider/keyword combination is enabled.
- static bool EventEnabled(GUID& providerID, INT64 keyword);
-
- // Write out an event. The event is identified by the providerID/eventID pair.
+ // Write out an event.
// Data is written as a serialized blob matching the ETW serialization conventions.
- static void WriteEvent(GUID& providerID, INT64 eventID, BYTE *pData, size_t length, bool sampleStack);
+ static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length);
- // Write out a sample profile event with the specified stack.
- static void WriteSampleProfileEvent(Thread *pThread, StackContents &stackContents);
+ // Write out a sample profile event.
+ static void WriteSampleProfileEvent(SampleProfilerEventInstance &instance);
// Get the managed call stack for the current thread.
static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
// Callback function for the stack walker. For each frame walked, this callback is invoked.
static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData);
- static CrstStatic s_initCrst;
+ // Get the configuration object.
+ // This is called directly by the EventPipeProvider constructor to register the new provider.
+ static EventPipeConfiguration* GetConfiguration();
+
+ // Get the event pipe configuration lock.
+ static CrstStatic* GetLock();
+
+ static CrstStatic s_configCrst;
static bool s_tracingInitialized;
- static bool s_tracingEnabled;
+ static EventPipeConfiguration *s_pConfig;
+ static EventPipeFile *s_pFile;
static EventPipeJsonFile *s_pJsonFile;
};
+#endif // FEATURE_PERFTRACING
+
#endif // __EVENTPIPE_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeeventinstance.h"
+#include "eventpipeprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+// {5291C09C-2660-4D6A-83A3-C383FD020DEC}
+const GUID EventPipeConfiguration::s_configurationProviderID =
+ { 0x5291c09c, 0x2660, 0x4d6a, { 0x83, 0xa3, 0xc3, 0x83, 0xfd, 0x2, 0xd, 0xec } };
+
+EventPipeConfiguration::EventPipeConfiguration()
+{
+ STANDARD_VM_CONTRACT;
+
+ m_pProviderList = new SList<SListElem<EventPipeProvider*>>();
+}
+
+EventPipeConfiguration::~EventPipeConfiguration()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_pProviderList != NULL)
+ {
+ delete(m_pProviderList);
+ m_pProviderList = NULL;
+ }
+}
+
+void EventPipeConfiguration::Initialize()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Create the configuration provider.
+ m_pConfigProvider = new EventPipeProvider(s_configurationProviderID);
+
+ // Create the metadata event.
+ m_pMetadataEvent = m_pConfigProvider->AddEvent(
+ 0, /* keywords */
+ 0, /* eventID */
+ 0, /* eventVersion */
+ EventPipeEventLevel::Critical,
+ false); /* needStack */
+}
+
+bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the lock before manipulating the provider list.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ // See if we've already registered this provider.
+ EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderID());
+ if(pExistingProvider != NULL)
+ {
+ return false;
+ }
+
+ // The provider has not been registered, so register it.
+ m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider));
+
+ // TODO: Set the provider configuration and enable it if we know
+ // anything about the provider before it is registered.
+
+ return true;
+}
+
+bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the lock before manipulating the provider list.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ // Find the provider.
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ if(pElem->GetValue() == &provider)
+ {
+ break;
+ }
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+
+ // If we found the provider, remove it.
+ if(pElem != NULL)
+ {
+ if(m_pProviderList->FindAndRemove(pElem) != NULL)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+EventPipeProvider* EventPipeConfiguration::GetProvider(const GUID &providerID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the lock before touching the provider list to ensure no one tries to
+ // modify the list.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ return GetProviderNoLock(providerID);
+}
+
+EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const GUID &providerID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ EventPipeProvider *pProvider = pElem->GetValue();
+ if(pProvider->GetProviderID() == providerID)
+ {
+ return pProvider;
+ }
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+
+ return NULL;
+}
+
+void EventPipeConfiguration::Enable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ // Lock must be held by EventPipe::Enable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ // TODO: Only enable the providers that have been explicitly enabled with specified keywords/level.
+ EventPipeProvider *pProvider = pElem->GetValue();
+ pProvider->SetConfiguration(true /* providerEnabled */, 0xFFFFFFFFFFFFFFFF /* keywords */, EventPipeEventLevel::Verbose /* level */);
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+
+}
+
+void EventPipeConfiguration::Disable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ // Lock must be held by EventPipe::Disable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ EventPipeProvider *pProvider = pElem->GetValue();
+ pProvider->SetConfiguration(false /* providerEnabled */, 0 /* keywords */, EventPipeEventLevel::Critical /* level */);
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+}
+
+EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEvent &sourceEvent, BYTE *pPayloadData, unsigned int payloadLength)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // The payload of the event should contain:
+ // - GUID ProviderID.
+ // - unsigned int EventID.
+ // - unsigned int EventVersion.
+ // - Optional event description payload.
+
+ // Calculate the size of the event.
+ const GUID &providerID = sourceEvent.GetProvider()->GetProviderID();
+ unsigned int eventID = sourceEvent.GetEventID();
+ unsigned int eventVersion = sourceEvent.GetEventVersion();
+ unsigned int instancePayloadSize = sizeof(providerID) + sizeof(eventID) + sizeof(eventVersion) + payloadLength;
+
+ // Allocate the payload.
+ BYTE *pInstancePayload = new BYTE[instancePayloadSize];
+
+ // Fill the buffer with the payload.
+ BYTE *currentPtr = pInstancePayload;
+
+ // Write the provider ID.
+ memcpy(currentPtr, (BYTE*)&providerID, sizeof(providerID));
+ currentPtr += sizeof(providerID);
+
+ // Write the event ID.
+ memcpy(currentPtr, &eventID, sizeof(eventID));
+ currentPtr += sizeof(eventID);
+
+ // Write the event version.
+ memcpy(currentPtr, &eventVersion, sizeof(eventVersion));
+ currentPtr += sizeof(eventVersion);
+
+ // Write the incoming payload data.
+ memcpy(currentPtr, pPayloadData, payloadLength);
+
+ // Construct the event instance.
+ EventPipeEventInstance *pInstance = new EventPipeEventInstance(
+ *m_pMetadataEvent,
+ GetCurrentThreadId(),
+ pInstancePayload,
+ instancePayloadSize);
+
+ return pInstance;
+}
+
+#endif // FEATURE_PERFTRACING
--- /dev/null
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_CONFIGURATION_H__
+#define __EVENTPIPE_CONFIGURATION_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "slist.h"
+
+class EventPipeEvent;
+class EventPipeEventInstance;
+class EventPipeProvider;
+
+class EventPipeConfiguration
+{
+public:
+
+ EventPipeConfiguration();
+ ~EventPipeConfiguration();
+
+ // Perform initialization that cannot be performed in the constructor.
+ void Initialize();
+
+ // Register a provider.
+ bool RegisterProvider(EventPipeProvider &provider);
+
+ // Unregister a provider.
+ bool UnregisterProvider(EventPipeProvider &provider);
+
+ // Get the provider with the specified provider ID if it exists.
+ EventPipeProvider* GetProvider(const GUID &providerID);
+
+ // Enable the event pipe.
+ void Enable();
+
+ // Disable the event pipe.
+ void Disable();
+
+ // Get the event used to write metadata to the event stream.
+ EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEvent &sourceEvent, BYTE *pPayloadData = NULL, unsigned int payloadLength = 0);
+
+private:
+
+ // Get the provider without taking the lock.
+ EventPipeProvider* GetProviderNoLock(const GUID &providerID);
+
+ // The list of event pipe providers.
+ SList<SListElem<EventPipeProvider*>> *m_pProviderList;
+
+ // The provider used to write configuration events to the event stream.
+ EventPipeProvider *m_pConfigProvider;
+
+ // The event used to write event information to the event stream.
+ EventPipeEvent *m_pMetadataEvent;
+
+ // The provider ID for the configuration event pipe provider.
+ // This provider is used to emit configuration events.
+ static const GUID s_configurationProviderID;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_CONFIGURATION_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeevent.h"
+#include "eventpipeprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeEvent::EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pProvider = &provider;
+ m_keywords = keywords;
+ m_eventID = eventID;
+ m_eventVersion = eventVersion;
+ m_level = level;
+ m_needStack = needStack;
+ m_enabled = false;
+}
+
+EventPipeProvider* EventPipeEvent::GetProvider() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pProvider;
+}
+
+INT64 EventPipeEvent::GetKeywords() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_keywords;
+}
+
+unsigned int EventPipeEvent::GetEventID() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_eventID;
+}
+
+unsigned int EventPipeEvent::GetEventVersion() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_eventVersion;
+}
+
+EventPipeEventLevel EventPipeEvent::GetLevel() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_level;
+}
+
+bool EventPipeEvent::NeedStack() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_needStack;
+}
+
+bool EventPipeEvent::IsEnabled() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_enabled;
+}
+
+void EventPipeEvent::RefreshState()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_enabled = m_pProvider->EventEnabled(m_keywords, m_level);
+}
+
+#endif // FEATURE_PERFTRACING
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_EVENT_H__
+#define __EVENTPIPE_EVENT_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipeprovider.h"
+
+class EventPipeEvent
+{
+ // Declare friends.
+ friend class EventPipeProvider;
+
+private:
+
+ // The provider that contains the event.
+ EventPipeProvider *m_pProvider;
+
+ // Bit vector containing the keywords that enable the event.
+ INT64 m_keywords;
+
+ // The ID (within the provider) of the event.
+ unsigned int m_eventID;
+
+ // The version of the event.
+ unsigned int m_eventVersion;
+
+ // The verbosity of the event.
+ EventPipeEventLevel m_level;
+
+ // True if a call stack should be captured when writing the event.
+ bool m_needStack;
+
+ // True if the event is current enabled.
+ bool m_enabled;
+
+ // Refreshes the runtime state for this event.
+ // Called by EventPipeProvider when the provider configuration changes.
+ void RefreshState();
+
+ // Only EventPipeProvider can create events.
+ // The provider is responsible for allocating and freeing events.
+ EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack);
+
+public:
+
+ // Get the provider associated with this event.
+ EventPipeProvider* GetProvider() const;
+
+ // Get the keywords that enable the event.
+ INT64 GetKeywords() const;
+
+ // Get the ID (within the provider) of the event.
+ unsigned int GetEventID() const;
+
+ // Get the version of the event.
+ unsigned int GetEventVersion() const;
+
+ // Get the verbosity of the event.
+ EventPipeEventLevel GetLevel() const;
+
+ // True if a call stack should be captured when writing the event.
+ bool NeedStack() const;
+
+ // True if the event is currently enabled.
+ bool IsEnabled() const;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_EVENT_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeeventinstance.h"
+#include "eventpipejsonfile.h"
+#include "fastserializer.h"
+#include "sampleprofiler.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeEventInstance::EventPipeEventInstance(
+ EventPipeEvent &event,
+ DWORD threadID,
+ BYTE *pData,
+ unsigned int length)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pEvent = &event;
+ m_threadID = threadID;
+ m_pData = pData;
+ m_dataLength = length;
+ QueryPerformanceCounter(&m_timeStamp);
+
+ if(event.NeedStack())
+ {
+ EventPipe::WalkManagedStackForCurrentThread(m_stackContents);
+ }
+}
+
+StackContents* EventPipeEventInstance::GetStack()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return &m_stackContents;
+}
+
+EventPipeEvent* EventPipeEventInstance::GetEvent() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pEvent;
+}
+
+BYTE* EventPipeEventInstance::GetData() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pData;
+}
+
+unsigned int EventPipeEventInstance::GetLength() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_dataLength;
+}
+
+void EventPipeEventInstance::FastSerialize(FastSerializer *pSerializer, StreamLabel metadataLabel)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ // Useful for diagnosing serialization bugs.
+ const unsigned int value = 0xDEADBEEF;
+ pSerializer->WriteBuffer((BYTE*)&value, sizeof(value));
+#endif
+
+ // Calculate the size of the total payload so that it can be written to the file.
+ unsigned int payloadLength =
+ sizeof(metadataLabel) +
+ sizeof(m_threadID) + // Thread ID
+ sizeof(m_timeStamp) + // TimeStamp
+ m_dataLength + // Event payload data length
+ m_stackContents.GetSize(); // Stack payload size
+
+ // Write the size of the event to the file.
+ pSerializer->WriteBuffer((BYTE*)&payloadLength, sizeof(payloadLength));
+
+ // Write the metadata label.
+ pSerializer->WriteBuffer((BYTE*)&metadataLabel, sizeof(metadataLabel));
+
+ // Write the thread ID.
+ pSerializer->WriteBuffer((BYTE*)&m_threadID, sizeof(m_threadID));
+
+ // Write the timestamp.
+ pSerializer->WriteBuffer((BYTE*)&m_timeStamp, sizeof(m_timeStamp));
+
+ // Write the event data payload.
+ if(m_dataLength > 0)
+ {
+ pSerializer->WriteBuffer(m_pData, m_dataLength);
+ }
+
+ // Write the stack if present.
+ if(m_stackContents.GetSize() > 0)
+ {
+ pSerializer->WriteBuffer(m_stackContents.GetPointer(), m_stackContents.GetSize());
+ }
+}
+
+void EventPipeEventInstance::SerializeToJsonFile(EventPipeJsonFile *pFile)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(pFile == NULL)
+ {
+ return;
+ }
+
+ EX_TRY
+ {
+ const unsigned int guidSize = 39;
+ WCHAR wszProviderID[guidSize];
+ if(!StringFromGUID2(m_pEvent->GetProvider()->GetProviderID(), wszProviderID, guidSize))
+ {
+ wszProviderID[0] = '\0';
+ }
+
+ // Strip off the {}.
+ StackScratchBuffer scratch;
+ SString guidStr(&wszProviderID[1], guidSize-3);
+
+ SString message;
+ message.Printf("Provider=%s/EventID=%d/Version=%d", guidStr.GetANSI(scratch), m_pEvent->GetEventID(), m_pEvent->GetEventVersion());
+ pFile->WriteEvent(m_timeStamp, m_threadID, message, m_stackContents);
+ }
+ EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
+}
+
+SampleProfilerEventInstance::SampleProfilerEventInstance(Thread *pThread)
+ :EventPipeEventInstance(*SampleProfiler::s_pThreadTimeEvent, pThread->GetOSThreadId(), NULL, 0)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+#endif // FEATURE_PERFTRACING
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_EVENTINSTANCE_H__
+#define __EVENTPIPE_EVENTINSTANCE_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipe.h"
+#include "eventpipeevent.h"
+#include "fastserializableobject.h"
+#include "fastserializer.h"
+
+class EventPipeEventInstance
+{
+
+public:
+
+ EventPipeEventInstance(EventPipeEvent &event, DWORD threadID, BYTE *pData, unsigned int length);
+
+ // Get the event associated with this instance.
+ EventPipeEvent* GetEvent() const;
+
+ // Get the stack contents object to either read or write to it.
+ StackContents* GetStack();
+
+ // Get a pointer to the data payload.
+ BYTE* GetData() const;
+
+ // Get the length of the data.
+ unsigned int GetLength() const;
+
+ // Serialize this object using FastSerialization.
+ void FastSerialize(FastSerializer *pSerializer, StreamLabel metadataLabel);
+
+ // Serialize this event to the JSON file.
+ void SerializeToJsonFile(EventPipeJsonFile *pFile);
+
+protected:
+
+ EventPipeEvent *m_pEvent;
+ DWORD m_threadID;
+ LARGE_INTEGER m_timeStamp;
+
+ BYTE *m_pData;
+ unsigned int m_dataLength;
+ StackContents m_stackContents;
+};
+
+// A specific type of event instance for use by the SampleProfiler.
+// This is needed because the SampleProfiler knows how to walk stacks belonging
+// to threads other than the current thread.
+class SampleProfilerEventInstance : public EventPipeEventInstance
+{
+
+public:
+
+ SampleProfilerEventInstance(Thread *pThread);
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_EVENTINSTANCE_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipefile.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeFile::EventPipeFile(SString &outputFilePath)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pSerializer = new FastSerializer(outputFilePath, *this);
+ m_serializationLock.Init(LOCK_TYPE_DEFAULT);
+ m_pMetadataLabels = new MapSHashWithRemove<EventPipeEvent*, StreamLabel>();
+
+ // File start time information.
+ GetSystemTime(&m_fileOpenSystemTime);
+ QueryPerformanceCounter(&m_fileOpenTimeStamp);
+ QueryPerformanceFrequency(&m_timeStampFrequency);
+
+ // Write a forward reference to the beginning of the event stream.
+ // This also allows readers to know where the event stream ends and skip it if needed.
+ m_beginEventsForwardReferenceIndex = m_pSerializer->AllocateForwardReference();
+ m_pSerializer->WriteForwardReference(m_beginEventsForwardReferenceIndex);
+
+ // Write the header information into the file.
+
+ // Write the current date and time.
+ m_pSerializer->WriteBuffer((BYTE*)&m_fileOpenSystemTime, sizeof(m_fileOpenSystemTime));
+
+ // Write FileOpenTimeStamp
+ m_pSerializer->WriteBuffer((BYTE*)&m_fileOpenTimeStamp, sizeof(m_fileOpenTimeStamp));
+
+ // Write ClockFrequency
+ m_pSerializer->WriteBuffer((BYTE*)&m_timeStampFrequency, sizeof(m_timeStampFrequency));
+}
+
+EventPipeFile::~EventPipeFile()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Mark the end of the event stream.
+ StreamLabel currentLabel = m_pSerializer->GetStreamLabel();
+
+ // Define the event start forward reference.
+ m_pSerializer->DefineForwardReference(m_beginEventsForwardReferenceIndex, currentLabel);
+
+ // Close the serializer.
+ if(m_pSerializer != NULL)
+ {
+ delete(m_pSerializer);
+ m_pSerializer = NULL;
+ }
+}
+
+void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the serialization lock.
+ SpinLockHolder _slh(&m_serializationLock);
+
+ // Check to see if we've seen this event type before.
+ // If not, then write the event metadata to the event stream first.
+ StreamLabel metadataLabel = GetMetadataLabel(*instance.GetEvent());
+ if(metadataLabel == 0)
+ {
+ EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(*instance.GetEvent());
+
+ metadataLabel = m_pSerializer->GetStreamLabel();
+ pMetadataInstance->FastSerialize(m_pSerializer, (StreamLabel)0); // 0 breaks recursion and represents the metadata event.
+
+ SaveMetadataLabel(*instance.GetEvent(), metadataLabel);
+
+ delete (pMetadataInstance->GetData());
+ delete (pMetadataInstance);
+ }
+
+ // Write the event to the stream.
+ instance.FastSerialize(m_pSerializer, metadataLabel);
+}
+
+StreamLabel EventPipeFile::GetMetadataLabel(EventPipeEvent &event)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ StreamLabel outLabel;
+ if(m_pMetadataLabels->Lookup(&event, &outLabel))
+ {
+ _ASSERTE(outLabel != 0);
+ return outLabel;
+ }
+
+ return 0;
+}
+
+void EventPipeFile::SaveMetadataLabel(EventPipeEvent &event, StreamLabel label)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(label > 0);
+ }
+ CONTRACTL_END;
+
+ // If a pre-existing metadata label exists, remove it.
+ StreamLabel outLabel;
+ if(m_pMetadataLabels->Lookup(&event, &outLabel))
+ {
+ m_pMetadataLabels->Remove(&event);
+ }
+
+ // Add the metadata label.
+ m_pMetadataLabels->Add(&event, label);
+}
+
+#endif // FEATURE_PERFTRACING
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+#ifndef __EVENTPIPE_FILE_H__
+#define __EVENTPIPE_FILE_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipe.h"
+#include "eventpipeeventinstance.h"
+#include "fastserializableobject.h"
+#include "fastserializer.h"
+
+class EventPipeFile : public FastSerializableObject
+{
+ public:
+ EventPipeFile(SString &outputFilePath);
+ ~EventPipeFile();
+
+ // Write an event to the file.
+ void WriteEvent(EventPipeEventInstance &instance);
+
+ // Serialize this object.
+ // Not supported - this is the entry object for the trace,
+ // which means that the contents hasn't yet been created.
+ void FastSerialize(FastSerializer *pSerializer)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(!"This function should never be called!");
+ }
+
+ // Get the type name of this object.
+ const char* GetTypeName()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return "Microsoft.DotNet.Runtime.EventPipeFile";
+ }
+
+ private:
+
+ // Get the metadata address in the file for an event.
+ // The return value can be written into the file as a back-pointer to the event metadata.
+ StreamLabel GetMetadataLabel(EventPipeEvent &event);
+
+ // Save the metadata address in the file for an event.
+ void SaveMetadataLabel(EventPipeEvent &event, StreamLabel label);
+
+ // The object responsible for serialization.
+ FastSerializer *m_pSerializer;
+
+ // The system time when the file was opened.
+ SYSTEMTIME m_fileOpenSystemTime;
+
+ // The timestamp when the file was opened. Used for calculating file-relative timestamps.
+ LARGE_INTEGER m_fileOpenTimeStamp;
+
+ // The frequency of the timestamps used for this file.
+ LARGE_INTEGER m_timeStampFrequency;
+
+ // The forward reference index that marks the beginning of the event stream.
+ unsigned int m_beginEventsForwardReferenceIndex;
+
+ // The serialization which is responsible for making sure only a single event
+ // or block of events gets written to the file at once.
+ SpinLock m_serializationLock;
+
+ // Hashtable of metadata labels.
+ MapSHashWithRemove<EventPipeEvent*, StreamLabel> *m_pMetadataLabels;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_FILE_H__
#include "common.h"
#include "eventpipejsonfile.h"
+#ifdef FEATURE_PERFTRACING
+
EventPipeJsonFile::EventPipeJsonFile(SString &outFilePath)
{
CONTRACTL
}
CONTRACTL_END;
+ m_writeErrorEncountered = false;
m_pFileStream = new CFileStream();
if(FAILED(m_pFileStream->OpenForWrite(outFilePath)))
{
}
}
-void EventPipeJsonFile::WriteEvent(CommonEventFields &commonFields, SString &message, StackContents &stackContents)
+void EventPipeJsonFile::WriteEvent(EventPipeEventInstance &instance)
+{
+ STANDARD_VM_CONTRACT;
+
+ instance.SerializeToJsonFile(this);
+}
+
+void EventPipeJsonFile::WriteEvent(LARGE_INTEGER timeStamp, DWORD threadID, SString &message, StackContents &stackContents)
{
STANDARD_VM_CONTRACT;
// Convert the timestamp from a QPC value to a trace-relative timestamp.
double millisecondsSinceTraceStart = 0.0;
- if(commonFields.TimeStamp.QuadPart != m_fileOpenTimeStamp.QuadPart)
+ if(timeStamp.QuadPart != m_fileOpenTimeStamp.QuadPart)
{
LARGE_INTEGER elapsedNanoseconds;
- elapsedNanoseconds.QuadPart = commonFields.TimeStamp.QuadPart - m_fileOpenTimeStamp.QuadPart;
+ elapsedNanoseconds.QuadPart = timeStamp.QuadPart - m_fileOpenTimeStamp.QuadPart;
millisecondsSinceTraceStart = elapsedNanoseconds.QuadPart / 1000000.0;
}
StackScratchBuffer scratch;
SString threadFrame;
- threadFrame.Printf("Thread (%d)", commonFields.ThreadID);
+ threadFrame.Printf("Thread (%d)", threadID);
SString event;
event.Printf("{\"Time\" : \"%f\", \"Metric\" : \"1\",\n\"Stack\": [\n\"%s\",\n%s\"%s\"]},", millisecondsSinceTraceStart, message.GetANSI(scratch), strCallStack.GetANSI(scratch), threadFrame.GetANSI(scratch));
Write(event);
resultStr.Append(frameStr);
}
}
+
+#endif // FEATURE_PERFTRACING
#ifndef __EVENTPIPE_JSONFILE_H__
#define __EVENTPIPE_JSONFILE_H__
+#ifdef FEATURE_PERFTRACING
+
#include "common.h"
#include "eventpipe.h"
+#include "eventpipeeventinstance.h"
#include "fstream.h"
class EventPipeJsonFile
EventPipeJsonFile(SString &outFilePath);
~EventPipeJsonFile();
+ // Write an event instance.
+ void WriteEvent(EventPipeEventInstance &instance);
+
// Write an event with the specified message and stack.
- void WriteEvent(CommonEventFields &commonFields, SString &message, StackContents &stackContents);
+ void WriteEvent(LARGE_INTEGER timeStamp, DWORD threadID, SString &message, StackContents &stackContents);
private:
LARGE_INTEGER m_fileOpenTimeStamp;
};
+#endif // FEATURE_PERFTRACING
+
#endif // __EVENTPIPE_JSONFILE_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeevent.h"
+#include "eventpipeprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeProvider::EventPipeProvider(const GUID &providerID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_providerID = providerID;
+ m_enabled = false;
+ m_keywords = 0;
+ m_providerLevel = EventPipeEventLevel::Critical;
+ m_pEventList = new SList<SListElem<EventPipeEvent*>>();
+ m_pCallbackFunction = NULL;
+ m_pCallbackData = NULL;
+
+ // Register the provider.
+ EventPipeConfiguration* pConfig = EventPipe::GetConfiguration();
+ _ASSERTE(pConfig != NULL);
+ pConfig->RegisterProvider(*this);
+}
+
+EventPipeProvider::~EventPipeProvider()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Unregister the provider.
+ // This call is re-entrant.
+ EventPipeConfiguration* pConfig = EventPipe::GetConfiguration();
+ _ASSERTE(pConfig != NULL);
+ pConfig->UnregisterProvider(*this);
+
+ // Free all of the events.
+ if(m_pEventList != NULL)
+ {
+ // Take the lock before manipulating the list.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ SListElem<EventPipeEvent*> *pElem = m_pEventList->GetHead();
+ while(pElem != NULL)
+ {
+ EventPipeEvent *pEvent = pElem->GetValue();
+ delete pEvent;
+
+ pElem = m_pEventList->GetNext(pElem);
+ }
+
+ delete m_pEventList;
+ m_pEventList = NULL;
+ }
+}
+
+const GUID& EventPipeProvider::GetProviderID() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_providerID;
+}
+
+bool EventPipeProvider::Enabled() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_enabled;
+}
+
+bool EventPipeProvider::EventEnabled(INT64 keywords) const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // The event is enabled if:
+ // - The provider is enabled.
+ // - The event keywords are unspecified in the manifest (== 0) or when masked with the enabled config are != 0.
+ return (Enabled() && ((keywords == 0) || ((m_keywords & keywords) != 0)));
+}
+
+bool EventPipeProvider::EventEnabled(INT64 keywords, EventPipeEventLevel eventLevel) const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // The event is enabled if:
+ // - The provider is enabled.
+ // - The event keywords are unspecified in the manifest (== 0) or when masked with the enabled config are != 0.
+ // - The event level is LogAlways or the provider's verbosity level is set to greater than the event's verbosity level in the manifest.
+ return (EventEnabled(keywords) &&
+ ((eventLevel == EventPipeEventLevel::LogAlways) || (m_providerLevel >= eventLevel)));
+}
+
+void EventPipeProvider::SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ m_enabled = providerEnabled;
+ m_keywords = keywords;
+ m_providerLevel = providerLevel;
+
+ RefreshAllEvents();
+ InvokeCallback();
+}
+
+EventPipeEvent* EventPipeProvider::AddEvent(INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Create the event.
+ EventPipeEvent *pEvent = new EventPipeEvent(
+ *this,
+ keywords,
+ eventID,
+ eventVersion,
+ level,
+ needStack);
+
+ // Add it to the list of events.
+ AddEvent(*pEvent);
+ return pEvent;
+}
+
+void EventPipeProvider::AddEvent(EventPipeEvent &event)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the config lock before inserting a new event.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ m_pEventList->InsertTail(new SListElem<EventPipeEvent*>(&event));
+}
+
+void EventPipeProvider::RegisterCallback(EventPipeCallback pCallbackFunction, void *pData)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the config lock before setting the callback.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ if(m_pCallbackFunction == NULL)
+ {
+ m_pCallbackFunction = pCallbackFunction;
+ m_pCallbackData = pData;
+ }
+}
+
+void EventPipeProvider::UnregisterCallback(EventPipeCallback pCallbackFunction)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the config lock before setting the callback.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ if(m_pCallbackFunction == pCallbackFunction)
+ {
+ m_pCallbackFunction = NULL;
+ m_pCallbackData = NULL;
+ }
+}
+
+void EventPipeProvider::InvokeCallback()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ if(m_pCallbackFunction != NULL)
+ {
+ (*m_pCallbackFunction)(
+ &m_providerID,
+ m_enabled,
+ (UCHAR) m_providerLevel,
+ m_keywords,
+ 0 /* matchAllKeywords */,
+ NULL /* FilterData */,
+ m_pCallbackData /* CallbackContext */);
+ }
+}
+
+void EventPipeProvider::RefreshAllEvents()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeEvent*> *pElem = m_pEventList->GetHead();
+ while(pElem != NULL)
+ {
+ EventPipeEvent *pEvent = pElem->GetValue();
+ pEvent->RefreshState();
+
+ pElem = m_pEventList->GetNext(pElem);
+ }
+}
+
+#endif // FEATURE_PERFTRACING
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_PROVIDER_H__
+#define __EVENTPIPE_PROVIDER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "slist.h"
+
+class EventPipeEvent;
+
+// Define the event pipe callback to match the ETW callback signature.
+typedef void (*EventPipeCallback)(
+ LPCGUID SourceID,
+ ULONG IsEnabled,
+ UCHAR Level,
+ ULONGLONG MatchAnyKeywords,
+ ULONGLONG MatchAllKeywords,
+ void *FilterData,
+ void *CallbackContext);
+
+enum class EventPipeEventLevel
+{
+ LogAlways,
+ Critical,
+ Error,
+ Warning,
+ Informational,
+ Verbose
+};
+
+class EventPipeProvider
+{
+ // Declare friends.
+ friend class EventPipeConfiguration;
+
+private:
+ // The GUID of the provider.
+ GUID m_providerID;
+
+ // True if the provider is enabled.
+ bool m_enabled;
+
+ // Bit vector containing the currently enabled keywords.
+ INT64 m_keywords;
+
+ // The current verbosity of the provider.
+ EventPipeEventLevel m_providerLevel;
+
+ // List of every event currently associated with the provider.
+ // New events can be added on-the-fly.
+ SList<SListElem<EventPipeEvent*>> *m_pEventList;
+
+ // The optional provider callback.
+ EventPipeCallback m_pCallbackFunction;
+
+ // The optional provider callback data pointer.
+ void *m_pCallbackData;
+
+public:
+
+ EventPipeProvider(const GUID &providerID);
+ ~EventPipeProvider();
+
+ // Get the provider ID.
+ const GUID& GetProviderID() const;
+
+ // Determine if the provider is enabled.
+ bool Enabled() const;
+
+ // Determine if the specified keywords are enabled.
+ bool EventEnabled(INT64 keywords) const;
+
+ // Determine if the specified keywords and level match the configuration.
+ bool EventEnabled(INT64 keywords, EventPipeEventLevel eventLevel) const;
+
+ // Create a new event.
+ EventPipeEvent* AddEvent(INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack);
+
+ // Register a callback with the provider to be called on state change.
+ void RegisterCallback(EventPipeCallback pCallbackFunction, void *pData);
+
+ // Unregister a callback.
+ void UnregisterCallback(EventPipeCallback pCallbackFunction);
+
+private:
+
+ // Add an event to the provider.
+ void AddEvent(EventPipeEvent &event);
+
+ // Set the provider configuration (enable and disable sets of events).
+ // This is called by EventPipeConfiguration.
+ void SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel);
+
+ // Refresh the runtime state of all events.
+ void RefreshAllEvents();
+
+ // Invoke the provider callback.
+ void InvokeCallback();
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_PROVIDER_H__
ulHelperSize,
0,
methodFlags,
- NULL,
+ NULL,
pHelperName,
- NULL,
+ NULL,
GetClrInstanceId());
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __FASTSERIALIZABLE_OBJECT_H__
+#define __FASTSERIALIZABLE_OBJECT_H__
+
+#ifdef FEATURE_PERFTRACING
+
+class FastSerializer;
+
+class FastSerializableObject
+{
+
+public:
+
+ // Virtual destructor to ensure that derived class destructors get called.
+ virtual ~FastSerializableObject()
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ // Serialize the object using the specified serializer.
+ virtual void FastSerialize(FastSerializer *pSerializer) = 0;
+
+ // Get the type name for the current object.
+ virtual const char* GetTypeName() = 0;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // _FASTSERIALIZABLE_OBJECT_H__
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "fastserializer.h"
+
+#ifdef FEATURE_PERFTRACING
+
+FastSerializer::FastSerializer(SString &outputFilePath, FastSerializableObject &object)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_writeErrorEncountered = false;
+ m_pEntryObject = &object;
+ m_currentPos = 0;
+ m_nextForwardReference = 0;
+ m_pFileStream = new CFileStream();
+ if(FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
+ {
+ delete(m_pFileStream);
+ m_pFileStream = NULL;
+ return;
+ }
+
+ // Write the file header.
+ WriteFileHeader();
+
+ // Write the entry object.
+ WriteEntryObject();
+}
+
+FastSerializer::~FastSerializer()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Write the end of the entry object.
+ WriteTag(FastSerializerTags::EndObject);
+
+ // Write forward reference table.
+ StreamLabel forwardReferenceLabel = WriteForwardReferenceTable();
+
+ // Write trailer.
+ WriteTrailer(forwardReferenceLabel);
+
+ if(m_pFileStream != NULL)
+ {
+ delete(m_pFileStream);
+ m_pFileStream = NULL;
+ }
+}
+
+StreamLabel FastSerializer::GetStreamLabel() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (StreamLabel)m_currentPos;
+}
+
+void FastSerializer::WriteObject(FastSerializableObject *pObject)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pObject != NULL);
+ }
+ CONTRACTL_END;
+
+ // Write a BeginObject tag.
+ WriteTag(FastSerializerTags::BeginObject);
+
+ // Write object begin tag.
+ WriteSerializationType(pObject);
+
+ // Ask the object to serialize itself using the current serializer.
+ pObject->FastSerialize(this);
+
+ // Write object end tag.
+ WriteTag(FastSerializerTags::EndObject);
+}
+
+void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(pBuffer != NULL);
+ PRECONDITION(length > 0);
+ }
+ CONTRACTL_END;
+
+ if(m_writeErrorEncountered || m_pFileStream == NULL)
+ {
+ return;
+ }
+
+ EX_TRY
+ {
+ ULONG outCount;
+ m_pFileStream->Write(pBuffer, length, &outCount);
+
+#ifdef _DEBUG
+ size_t prevPos = m_currentPos;
+#endif
+ m_currentPos += outCount;
+#ifdef _DEBUG
+ _ASSERTE(prevPos < m_currentPos);
+#endif
+
+ if (length != outCount)
+ {
+ // This will cause us to stop writing to the file.
+ // The file will still remain open until shutdown so that we don't have to take a lock at this level when we touch the file stream.
+ m_writeErrorEncountered = true;
+ }
+ }
+ EX_CATCH
+ {
+ m_writeErrorEncountered = true;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+}
+
+void FastSerializer::WriteEntryObject()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Write begin entry object tag.
+ WriteTag(FastSerializerTags::BeginObject);
+
+ // Write the type information for the entry object.
+ WriteSerializationType(m_pEntryObject);
+
+ // The object is now initialized. Fields or other objects can now be written.
+}
+
+unsigned int FastSerializer::AllocateForwardReference()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(m_nextForwardReference < MaxForwardReferences);
+ }
+ CONTRACTL_END;
+
+ // TODO: Handle failure.
+
+ // Save the index.
+ int index = m_nextForwardReference;
+
+ // Allocate the forward reference and zero-fill it so that the reader
+ // will know if it was not properly defined.
+ m_forwardReferences[m_nextForwardReference++] = 0;
+
+ return index;
+}
+
+void FastSerializer::DefineForwardReference(unsigned int index, StreamLabel value)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(index < MaxForwardReferences-1);
+ }
+ CONTRACTL_END;
+
+ m_forwardReferences[index] = value;
+}
+
+void FastSerializer::WriteForwardReference(unsigned int index)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(index < MaxForwardReferences-1);
+ }
+ CONTRACTL_END;
+
+ WriteBuffer((BYTE*)&index, sizeof(index));
+}
+
+void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(pObject != NULL);
+ }
+ CONTRACTL_END;
+
+ // Write the BeginObject tag.
+ WriteTag(FastSerializerTags::BeginObject);
+
+ // Write a NullReferenceTag, which implies that the following fields belong to SerializationType.
+ WriteTag(FastSerializerTags::NullReference);
+
+ // Write the SerializationType version fields.
+ int serializationType[2];
+ serializationType[0] = 1; // Object Version.
+ serializationType[1] = 0; // Minimum Reader Version.
+ WriteBuffer((BYTE*) &serializationType, sizeof(serializationType));
+
+ // Write the SerializationType TypeName field.
+ const char *strTypeName = pObject->GetTypeName();
+ unsigned int length = (unsigned int)strlen(strTypeName);
+ WriteString(strTypeName, length);
+
+ // Write the EndObject tag.
+ WriteTag(FastSerializerTags::EndObject);
+}
+
+
+void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned int payloadLength)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ WriteBuffer((BYTE *)&tag, sizeof(tag));
+ if(payload != NULL)
+ {
+ _ASSERTE(payloadLength > 0);
+ WriteBuffer(payload, payloadLength);
+ }
+}
+
+
+void FastSerializer::WriteFileHeader()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ const char *strSignature = "!FastSerialization.1";
+ unsigned int length = (unsigned int)strlen(strSignature);
+ WriteString(strSignature, length);
+}
+
+void FastSerializer::WriteString(const char *strContents, unsigned int length)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Write the string length .
+ WriteBuffer((BYTE*) &length, sizeof(length));
+
+ // Write the string contents.
+ WriteBuffer((BYTE*) strContents, length);
+}
+
+StreamLabel FastSerializer::WriteForwardReferenceTable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Save the position of the start of the forward references table.
+ StreamLabel current = GetStreamLabel();
+
+ // Write the count of allocated references.
+ WriteBuffer((BYTE*) &m_nextForwardReference, sizeof(m_nextForwardReference));
+
+ // Write each of the allocated references.
+ WriteBuffer((BYTE*) m_forwardReferences, sizeof(StreamLabel) * m_nextForwardReference);
+
+ return current;
+}
+
+void FastSerializer::WriteTrailer(StreamLabel forwardReferencesTableStart)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Get the current location to mark the beginning of the trailer.
+ StreamLabel current = GetStreamLabel();
+
+ // Write the trailer, which contains the start of the forward references table.
+ WriteBuffer((BYTE*) &forwardReferencesTableStart, sizeof(forwardReferencesTableStart));
+
+ // Write the location of the trailer. This is the final piece of data written to the file,
+ // so that it can be easily found by a reader that can seek to the end of the file.
+ WriteBuffer((BYTE*) ¤t, sizeof(current));
+}
+
+#endif // FEATURE_PERFTRACING
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __FASTSERIALIZER_H__
+#define __FASTSERIALIZER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "fastserializableobject.h"
+#include "fstream.h"
+
+class FastSerializer;
+
+typedef unsigned int StreamLabel;
+
+enum class FastSerializerTags : BYTE
+{
+ Error, // To improve debugabilty, 0 is an illegal tag.
+ NullReference, // Tag for a null object forwardReference.
+ ObjectReference, // Followed by StreamLabel
+ ForwardReference, // Followed by an index (32-bit integer) into the Forward forwardReference array and a Type object
+ BeginObject, // Followed by Type object, object data, tagged EndObject
+ BeginPrivateObject, // Like beginObject, but not placed in interning table on deserialiation
+ EndObject, // Placed after an object to mark its end.
+ ForwardDefinition, // Followed by a forward forwardReference index and an object definition (BeginObject)
+ Byte,
+ Int16,
+ Int32,
+ Int64,
+ SkipRegion,
+ String,
+ Limit, // Just past the last valid tag, used for asserts.
+};
+
+class FastSerializer
+{
+public:
+
+ FastSerializer(SString &outputFilePath, FastSerializableObject &object);
+ ~FastSerializer();
+
+ StreamLabel GetStreamLabel() const;
+
+ void WriteObject(FastSerializableObject *pObject);
+ void WriteBuffer(BYTE *pBuffer, unsigned int length);
+ void WriteTag(FastSerializerTags tag, BYTE *payload = NULL, unsigned int payloadLength = 0);
+ void WriteString(const char *strContents, unsigned int length);
+
+ unsigned int AllocateForwardReference();
+ void DefineForwardReference(unsigned int index, StreamLabel value);
+ void WriteForwardReference(unsigned int index);
+
+private:
+
+ void WriteEntryObject();
+ void WriteSerializationType(FastSerializableObject *pObject);
+ void WriteFileHeader();
+ StreamLabel WriteForwardReferenceTable();
+ void WriteTrailer(StreamLabel forwardReferencesTableStart);
+
+ CFileStream *m_pFileStream;
+ bool m_writeErrorEncountered;
+ FastSerializableObject *m_pEntryObject;
+ size_t m_currentPos;
+
+ static const unsigned int MaxForwardReferences = 100;
+ StreamLabel m_forwardReferences[MaxForwardReferences];
+ unsigned int m_nextForwardReference;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __FASTSERIALIZER_H__
};
#include <poppack.h>
+#elif defined(_TARGET_ARM_)
+
+#include <pshpack1.h>
+struct CentralJumpCode {
+ BYTE m_ldrPC[4];
+ BYTE m_short[2];
+ MethodDescChunk *m_pChunk;
+ PCODE m_target;
+
+ inline void Setup(PCODE target, MethodDescChunk *pChunk) {
+ WRAPPER_NO_CONTRACT;
+
+ m_target = target;
+ m_pChunk = pChunk;
+ }
+
+ inline BOOL CheckTarget(TADDR target) {
+ WRAPPER_NO_CONTRACT;
+ return ((TADDR)m_target == target);
+ }
+}
+c_CentralJumpCode = {
+ { 0xDF, 0xF8, 0x08, 0xF0 }, // ldr pc, =pTarget
+ { 0x00, 0x00 }, // short offset for alignment
+ 0, // pChunk
+ 0 // pTarget
+};
+#include <poppack.h>
+
#else
#error Unsupported platform
#endif
static_assert_no_msg((TEP_CENTRAL_JUMP_SIZE & 1) == 0);
#define TEP_ENTRY_SIZE 4
+
+#ifdef _TARGET_ARM_
+
+#define TEP_HALF_ENTRY_SIZE (TEP_ENTRY_SIZE / 2)
+
+// Compact entry point on arm consists of two thumb instructions:
+// mov r12, pc
+// b CentralJumpCode
+
+// First instruction 0x46fc
+#define TEP_ENTRY_INSTR1_BYTE1 0xFC
+#define TEP_ENTRY_INSTR1_BYTE2 0x46
+
+// Mask for unconditional branch opcode
+#define TEP_ENTRY_INSTR2_MASK1 0xE0
+
+// Mask for opcode
+#define TEP_ENTRY_INSTR2_MASK2 0xF8
+
+// Bit used for ARM to identify compact entry points
+#define COMPACT_ENTRY_ARM_CODE 0x2
+
+/* static */ int MethodDescChunk::GetCompactEntryPointMaxCount ()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB / TEP_ENTRY_SIZE;
+}
+
+// Get offset from the start of current compact entry point to the CentralJumpCode
+static uint16_t DecodeOffsetFromBranchToCentralJump (uint16_t instr)
+{
+ int16_t offset = decodeUnconditionalBranchThumb ((LPBYTE) &instr);
+
+ offset += PC_REG_RELATIVE_OFFSET + TEP_HALF_ENTRY_SIZE;
+
+ _ASSERTE (offset >= TEP_ENTRY_SIZE && (offset % TEP_ENTRY_SIZE == 0));
+
+ return (uint16_t) offset;
+}
+
+#ifndef DACCESS_COMPILE
+
+// Encode branch instruction to central jump for current compact entry point
+static uint16_t EncodeBranchToCentralJump (int16_t offset)
+{
+ _ASSERTE (offset >= 0 && (offset % TEP_ENTRY_SIZE == 0));
+
+ offset += TEP_HALF_ENTRY_SIZE - PC_REG_RELATIVE_OFFSET;
+
+ uint16_t instr;
+ emitUnconditionalBranchThumb ((LPBYTE) &instr, offset);
+
+ return instr;
+}
+
+#endif // DACCESS_COMPILE
+
+#else // _TARGET_ARM_
+
#define TEP_MAX_BEFORE_INDEX (1 + (127 / TEP_ENTRY_SIZE))
#define TEP_MAX_BLOCK_INDEX (TEP_MAX_BEFORE_INDEX + (128 - TEP_CENTRAL_JUMP_SIZE) / TEP_ENTRY_SIZE)
#define TEP_FULL_BLOCK_SIZE (TEP_MAX_BLOCK_INDEX * TEP_ENTRY_SIZE + TEP_CENTRAL_JUMP_SIZE)
+#endif // _TARGET_ARM_
+
+BOOL MethodDescChunk::IsCompactEntryPointAtAddress(PCODE addr)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+
+ // Compact entrypoints start at odd addresses
+ return (addr & 1) != 0;
+
+#elif defined(_TARGET_ARM_)
+
+ // Compact entrypoints start at odd addresses (thumb) with second bit set to 1
+ uint8_t compactEntryPointMask = THUMB_CODE | COMPACT_ENTRY_ARM_CODE;
+ return (addr & compactEntryPointMask) == compactEntryPointMask;
+
+#else
+ #error Unsupported platform
+#endif
+}
+
//*******************************************************************************
/* static */ MethodDesc* MethodDescChunk::GetMethodDescFromCompactEntryPoint(PCODE addr, BOOL fSpeculative /*=FALSE*/)
{
// Always do consistency check in debug
if (fSpeculative INDEBUG(|| TRUE))
{
+#ifdef _TARGET_ARM_
+ if (!IsCompactEntryPointAtAddress(addr))
+#else // _TARGET_ARM_
if ((addr & 3) != 1 ||
*PTR_BYTE(addr) != X86_INSTR_MOV_AL ||
*PTR_BYTE(addr+2) != X86_INSTR_JMP_REL8)
+#endif // _TARGET_ARM_
{
if (fSpeculative) return NULL;
_ASSERTE(!"Unexpected code in temporary entrypoint");
}
}
+#ifdef _TARGET_ARM_
+
+ // On ARM compact entry points are thumb
+ _ASSERTE ((addr & THUMB_CODE) != 0);
+ addr = addr - THUMB_CODE;
+
+ // Get offset for CentralJumpCode from current compact entry point
+ PTR_UINT16 pBranchInstr = (PTR_UINT16(addr)) + 1;
+ uint16_t offset = DecodeOffsetFromBranchToCentralJump (*pBranchInstr);
+
+ TADDR centralJump = addr + offset;
+ int index = (centralJump - addr - TEP_ENTRY_SIZE) / TEP_ENTRY_SIZE;
+
+#else // _TARGET_ARM_
+
int index = *PTR_BYTE(addr+1);
TADDR centralJump = addr + 4 + *PTR_SBYTE(addr+3);
+#endif // _TARGET_ARM_
+
CentralJumpCode* pCentralJumpCode = PTR_CentralJumpCode(centralJump);
// Always do consistency check in debug
}
}
+#ifdef _TARGET_ARM_
+
+ _ASSERTE_IMPL(pCentralJumpCode->CheckTarget(GetPreStubCompactARMEntryPoint()));
+
+#else // _TARGET_ARM_
+
_ASSERTE_IMPL(pCentralJumpCode->CheckTarget(GetPreStubEntryPoint()));
+
+#endif // _TARGET_ARM_
}
+#ifdef _TARGET_ARM_
+ // Go through all MethodDesc in MethodDescChunk and find the one with the required index
+ PTR_MethodDescChunk pChunk = *((DPTR(PTR_MethodDescChunk))(centralJump + offsetof(CentralJumpCode, m_pChunk)));
+ TADDR pMD = PTR_HOST_TO_TADDR (pChunk->GetFirstMethodDesc ());
+
+ _ASSERTE (index >= 0 && index < ((int) pChunk->GetCount ()));
+
+ index = ((int) pChunk->GetCount ()) - 1 - index;
+
+ SIZE_T totalSize = 0;
+ int curIndex = 0;
+
+ while (index != curIndex)
+ {
+ SIZE_T sizeCur = (PTR_MethodDesc (pMD))->SizeOf ();
+ totalSize += sizeCur;
+
+ pMD += sizeCur;
+ ++curIndex;
+ }
+
+ return PTR_MethodDesc (pMD);
+#else // _TARGET_ARM_
return PTR_MethodDesc((TADDR)pCentralJumpCode->m_pBaseMD + index * MethodDesc::ALIGNMENT);
+#endif // _TARGET_ARM_
}
//*******************************************************************************
{
LIMITED_METHOD_DAC_CONTRACT;
+#ifdef _TARGET_ARM_
+
+ return COMPACT_ENTRY_ARM_CODE + count * TEP_ENTRY_SIZE + TEP_CENTRAL_JUMP_SIZE;
+
+#else // _TARGET_ARM_
+
int fullBlocks = count / TEP_MAX_BLOCK_INDEX;
int remainder = count % TEP_MAX_BLOCK_INDEX;
return 1 + (fullBlocks * TEP_FULL_BLOCK_SIZE) +
(remainder * TEP_ENTRY_SIZE) + ((remainder != 0) ? TEP_CENTRAL_JUMP_SIZE : 0);
+
+#endif // _TARGET_ARM_
}
#ifndef DACCESS_COMPILE
TADDR temporaryEntryPoints = (TADDR)pamTracker->Track(pLoaderAllocator->GetPrecodeHeap()->AllocAlignedMem(size, sizeof(TADDR)));
+#ifdef _TARGET_ARM_
+ BYTE* p = (BYTE*)temporaryEntryPoints + COMPACT_ENTRY_ARM_CODE;
+ int relOffset = count * TEP_ENTRY_SIZE - TEP_ENTRY_SIZE; // relative offset for the short jump
+
+ _ASSERTE (relOffset < MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB);
+#else // _TARGET_ARM_
// make the temporary entrypoints unaligned, so they are easy to identify
BYTE* p = (BYTE*)temporaryEntryPoints + 1;
+ int indexInBlock = TEP_MAX_BLOCK_INDEX; // recompute relOffset in first iteration
+ int relOffset = 0; // relative offset for the short jump
+#endif // _TARGET_ARM_
- int indexInBlock = TEP_MAX_BLOCK_INDEX; // recompute relOffset in first iteration
- int relOffset = 0; // relative offset for the short jump
MethodDesc * pBaseMD = 0; // index of the start of the block
MethodDesc * pMD = GetFirstMethodDesc();
for (int index = 0; index < count; index++)
{
+#ifdef _TARGET_ARM_
+
+ uint8_t *pMovInstrByte1 = (uint8_t *)p;
+ uint8_t *pMovInstrByte2 = (uint8_t *)p+1;
+ uint16_t *pBranchInstr = ((uint16_t *)p)+1;
+
+ *pMovInstrByte1 = TEP_ENTRY_INSTR1_BYTE1;
+ *pMovInstrByte2 = TEP_ENTRY_INSTR1_BYTE2;
+ *pBranchInstr = EncodeBranchToCentralJump ((int16_t) relOffset);
+
+ p += TEP_ENTRY_SIZE;
+
+#else // _TARGET_ARM_
+
if (indexInBlock == TEP_MAX_BLOCK_INDEX)
{
relOffset = (min(count - index, TEP_MAX_BEFORE_INDEX) - 1) * TEP_ENTRY_SIZE;
relOffset -= TEP_CENTRAL_JUMP_SIZE;
}
- relOffset -= TEP_ENTRY_SIZE;
indexInBlock++;
+#endif // _TARGET_ARM_
+
+ relOffset -= TEP_ENTRY_SIZE;
pMD = (MethodDesc *)((BYTE *)pMD + pMD->SizeOf());
}
+#ifdef _TARGET_ARM_
+
+ CentralJumpCode* pCode = (CentralJumpCode*)p;
+ memcpy(pCode, &c_CentralJumpCode, TEP_CENTRAL_JUMP_SIZE);
+ pCode->Setup (GetPreStubCompactARMEntryPoint(), this);
+
+ _ASSERTE(p + TEP_CENTRAL_JUMP_SIZE == (BYTE*)temporaryEntryPoints + size);
+
+#else // _TARGET_ARM_
+
_ASSERTE(p == (BYTE*)temporaryEntryPoints + size);
+#endif // _TARGET_ARM_
+
ClrFlushInstructionCache((LPVOID)temporaryEntryPoints, size);
SetHasCompactEntryPoints();
#ifdef HAS_COMPACT_ENTRYPOINTS
if (HasCompactEntryPoints())
{
+#ifdef _TARGET_ARM_
+
+ return GetTemporaryEntryPoints() + COMPACT_ENTRY_ARM_CODE + THUMB_CODE + index * TEP_ENTRY_SIZE;
+
+#else // _TARGET_ARM_
+
int fullBlocks = index / TEP_MAX_BLOCK_INDEX;
int remainder = index % TEP_MAX_BLOCK_INDEX;
return GetTemporaryEntryPoints() + 1 + (fullBlocks * TEP_FULL_BLOCK_SIZE) +
(remainder * TEP_ENTRY_SIZE) + ((remainder >= TEP_MAX_BEFORE_INDEX) ? TEP_CENTRAL_JUMP_SIZE : 0);
+
+#endif // _TARGET_ARM_
}
#endif // HAS_COMPACT_ENTRYPOINTS
// direct call to direct jump.
//
// We use (1) for x86 and (2) for 64-bit to get the best performance on each platform.
- //
+ // For ARM (1) is used.
TADDR AllocateCompactEntryPoints(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker);
static MethodDesc* GetMethodDescFromCompactEntryPoint(PCODE addr, BOOL fSpeculative = FALSE);
static SIZE_T SizeOfCompactEntryPoints(int count);
- static BOOL IsCompactEntryPointAtAddress(PCODE addr)
- {
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
- // Compact entrypoints start at odd addresses
- LIMITED_METHOD_DAC_CONTRACT;
- return (addr & 1) != 0;
-#else
- #error Unsupported platform
-#endif
- }
+ static BOOL IsCompactEntryPointAtAddress(PCODE addr);
+
+#ifdef _TARGET_ARM_
+ static int GetCompactEntryPointMaxCount ();
+#endif // _TARGET_ARM_
#endif // HAS_COMPACT_ENTRYPOINTS
FORCEINLINE PTR_MethodTable GetMethodTable()
DEFINE_PROPERTY(ARRAY, LENGTH, Length, Int)
DEFINE_METHOD(ARRAY, GET_DATA_PTR_OFFSET_INTERNAL, GetDataPtrOffsetInternal, IM_RetInt)
-#ifdef FEATURE_NONGENERIC_COLLECTIONS
-DEFINE_CLASS(ARRAY_LIST, Collections, ArrayList)
-DEFINE_METHOD(ARRAY_LIST, CTOR, .ctor, IM_RetVoid)
-DEFINE_METHOD(ARRAY_LIST, ADD, Add, IM_Obj_RetInt)
-#endif // FEATURE_NONGENERIC_COLLECTIONS
-
DEFINE_CLASS(ARRAY_WITH_OFFSET, Interop, ArrayWithOffset)
DEFINE_FIELD(ARRAY_WITH_OFFSET, M_ARRAY, m_array)
DEFINE_FIELD(ARRAY_WITH_OFFSET, M_OFFSET, m_offset)
// Note that these are just best guesses to save memory. If we guessed wrong,
// we will allocate a new exact type of precode in GetOrCreatePrecode.
BOOL fForcedPrecode = pFirstMD->RequiresStableEntryPoint(count > 1);
+
+#ifdef _TARGET_ARM_
+ if (pFirstMD->RequiresMethodDescCallingConvention(count > 1)
+ || count >= MethodDescChunk::GetCompactEntryPointMaxCount ())
+ {
+ // We do not pass method desc on scratch register
+ fForcedPrecode = TRUE;
+ }
+#endif // _TARGET_ARM_
+
if (!fForcedPrecode && (totalSize > MethodDescChunk::SizeOfCompactEntryPoints(count)))
return NULL;
#endif
align = 8;
#endif // _TARGET_X86_ && HAS_FIXUP_PRECODE
+#if defined(_TARGET_ARM_) && defined(HAS_COMPACT_ENTRYPOINTS)
+ // Precodes have to be aligned to allow fast compact entry points check
+ _ASSERTE (align >= sizeof(void*));
+#endif // _TARGET_ARM_ && HAS_COMPACT_ENTRYPOINTS
+
return align;
}
#ifndef DACCESS_COMPILE
EXTERN_C void STDCALL ThePreStub();
+
+#if defined(HAS_COMPACT_ENTRYPOINTS) && defined (_TARGET_ARM_)
+
+EXTERN_C void STDCALL ThePreStubCompactARM();
+
+#endif // defined(HAS_COMPACT_ENTRYPOINTS) && defined (_TARGET_ARM_)
+
EXTERN_C void STDCALL ThePreStubPatch();
//==========================================================================
}
#endif // defined(FEATURE_SHARE_GENERIC_CODE)
+#if defined (HAS_COMPACT_ENTRYPOINTS) && defined (_TARGET_ARM_)
+
+extern "C" MethodDesc * STDCALL PreStubGetMethodDescForCompactEntryPoint (PCODE pCode)
+{
+ _ASSERTE (pCode >= PC_REG_RELATIVE_OFFSET);
+
+ pCode = (PCODE) (pCode - PC_REG_RELATIVE_OFFSET + THUMB_CODE);
+
+ _ASSERTE (MethodDescChunk::IsCompactEntryPointAtAddress (pCode));
+
+ return MethodDescChunk::GetMethodDescFromCompactEntryPoint(pCode, FALSE);
+}
+
+#endif // defined (HAS_COMPACT_ENTRYPOINTS) && defined (_TARGET_ARM_)
+
//=============================================================================
// This function generates the real code for a method and installs it into
// the methoddesc. Usually ***BUT NOT ALWAYS***, this function runs only once
// See the LICENSE file in the project root for more information.
#include "common.h"
+#include "eventpipeeventinstance.h"
#include "sampleprofiler.h"
#include "hosting.h"
#include "threadsuspend.h"
+#ifdef FEATURE_PERFTRACING
+
Volatile<BOOL> SampleProfiler::s_profilingEnabled = false;
Thread* SampleProfiler::s_pSamplingThread = NULL;
+const GUID SampleProfiler::s_providerID = {0x3c530d44,0x97ae,0x513a,{0x1e,0x6d,0x78,0x3e,0x8f,0x8e,0x03,0xa9}}; // {3c530d44-97ae-513a-1e6d-783e8f8e03a9}
+EventPipeProvider* SampleProfiler::s_pEventPipeProvider = NULL;
+EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL;
CLREventStatic SampleProfiler::s_threadShutdownEvent;
#ifdef FEATURE_PAL
long SampleProfiler::s_samplingRateInNs = 1000000; // 1ms
#endif
-// Synchronization of multiple callers occurs in EventPipe::Enable.
void SampleProfiler::Enable()
{
CONTRACTL
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(s_pSamplingThread == NULL);
+ // Synchronization of multiple callers occurs in EventPipe::Enable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END;
+ if(s_pEventPipeProvider == NULL)
+ {
+ s_pEventPipeProvider = new EventPipeProvider(s_providerID);
+ s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent(
+ 0, /* keywords */
+ 0, /* eventID */
+ 0, /* eventVersion */
+ EventPipeEventLevel::Informational,
+ false /* NeedStack */);
+ }
+
s_profilingEnabled = true;
s_pSamplingThread = SetupUnstartedThread();
if(s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL))
}
}
-// Synchronization of multiple callers occurs in EventPipe::Disable.
void SampleProfiler::Disable()
{
CONTRACTL
THROWS;
GC_TRIGGERS;
MODE_ANY;
+ // Synchronization of multiple callers occurs in EventPipe::Disable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END;
CONTRACTL_END;
Thread *pThread = NULL;
- StackContents stackContents;
// Iterate over all managed threads.
// Assumes that the ThreadStoreLock is held because we've suspended all threads.
while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
{
+ SampleProfilerEventInstance instance(pThread);
+ StackContents &stackContents = *(instance.GetStack());
+
// Walk the stack and write it out as an event.
if(EventPipe::WalkManagedStackForThread(pThread, stackContents) && !stackContents.IsEmpty())
{
- EventPipe::WriteSampleProfileEvent(pThread, stackContents);
+ EventPipe::WriteSampleProfileEvent(instance);
}
}
}
+
+#endif // FEATURE_PERFTRACING
#ifndef __SAMPLEPROFILER_H__
#define __SAMPLEPROFILER_H__
+#ifdef FEATURE_PERFTRACING
+
#include "common.h"
#include "eventpipe.h"
class SampleProfiler
{
+
+ // Declare friends.
+ friend class SampleProfilerEventInstance;
+
public:
// Enable profiling.
// The sampling thread.
static Thread *s_pSamplingThread;
+ // The provider and event emitted by the profiler.
+ static const GUID s_providerID;
+ static EventPipeProvider *s_pEventPipeProvider;
+ static EventPipeEvent *s_pThreadTimeEvent;
+
// Thread shutdown event for synchronization between Disable() and the sampling thread.
static CLREventStatic s_threadShutdownEvent;
#endif
};
+#endif // FEATURE_PERFTRACING
+
#endif // __SAMPLEPROFILER_H__
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) do%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) filename=%24{fileToPrecompile}%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) echo Precompiling %24filename%0a</WrapperShContents>
- <WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) %24overlayDir/crossgen /Platform_Assemblies_Paths %24overlayDir %24filename 1> %24filename.stdout 2>%24filename.stderr%0a</WrapperShContents>
+ <WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) %24overlayDir/crossgen /Platform_Assemblies_Paths %24overlayDir /in %24filename /out %24overlayDir/temp.ni.dll 1> %24filename.stdout 2>%24filename.stderr%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) exitCode=%24%3F%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) if [ %24exitCode != 0 ]%3B then%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) if grep -q -e '(COR_E_ASSEMBLYEXPECTED)' %24filename.stderr%3B then%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) else%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) echo Unable to precompile %24filename.%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) fi%0a</WrapperShContents>
- <WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) fi%0a</WrapperShContents>
+ <WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) else%0a</WrapperShContents>
+ <WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) rm %24filename%0a</WrapperShContents>
+ <WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) mv %24overlayDir/temp.ni.dll %24filename%0a</WrapperShContents>
+ <WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) fi%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) rm %24filename.stdout%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) rm %24filename.stderr%0a</WrapperShContents>
<WrapperShContents Condition="'$(Crossgen)' == 'true'">$(WrapperShContents) done%0a</WrapperShContents>
<ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\Dev11\dev10_865840\dev10_865840\dev10_865840.cmd">
<Issue>2445</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\JitBlue\GitHub_11408\GitHub_11408\GitHub_11408.cmd">
+ <Issue>11408</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\TypeGeneratorTests\TypeGeneratorTest612\Generated612\*">
<Issue>6707</Issue>
</ExcludeList>
--- /dev/null
+@rem Licensed to the .NET Foundation under one or more agreements.
+@rem The .NET Foundation licenses this file to you under the MIT license.
+@rem See the LICENSE file in the project root for more information.
+
+@echo off
+
+set CORE_ROOT=%CD%\bin\tests\Windows_NT.%1.%2\Tests\Core_Root
+set FRAMEWORK_DIR=%CD%\bin\tests\Windows_NT.%1.%2\GC\Stress\Framework\ReliabilityFramework
+powershell "%CORE_ROOT%\CoreRun.exe %FRAMEWORK_DIR%\ReliabilityFramework.exe %FRAMEWORK_DIR%\testmix_gc.config | tee stdout.txt"
+
--- /dev/null
+#!/bin/bash
+
+export CORE_ROOT=`pwd`/bin/tests/Windows_NT.$1.$2/Tests/coreoverlay
+FRAMEWORK_DIR=`pwd`/bin/tests/Windows_NT.$1.$2/GC/Stress/Framework/ReliabilityFramework
+$CORE_ROOT/corerun $FRAMEWORK_DIR/ReliabilityFramework.exe $FRAMEWORK_DIR/testmix_gc.config | tee stdout.txt
+
set USAGE_DISPLAYED=
set SHOULD_UPLOAD_TO_BENCHVIEW=
set BENCHVIEW_PATH=
+ set COLLECTION_FLAGS=stopwatch
+ set ETW_COLLECTION=Off
+ set STABILITY_PREFIX=
call :parse_command_line_arguments %*
if defined USAGE_DISPLAYED exit /b %ERRORLEVEL%
call :set_test_architecture || exit /b 1
+ call :set_collection_config || exit /b 1
call :verify_benchview_tools || exit /b 1
call :verify_core_overlay || exit /b 1
call :set_perf_run_log || exit /b 1
if defined IS_SCENARIO_TEST (
call :run_cmd corerun.exe "%CORECLR_REPO%\sandbox\%BENCHNAME%.%TEST_FILE_EXT%" --perf:runid Perf 1>"%BENCHNAME_LOG_FILE_NAME%" 2>&1
) else (
- call :run_cmd corerun.exe PerfHarness.dll "%CORECLR_REPO%\sandbox\%BENCHNAME%.%TEST_FILE_EXT%" --perf:runid Perf 1>"%BENCHNAME_LOG_FILE_NAME%" 2>&1
+ call :run_cmd %STABILITY_PREFIX% corerun.exe PerfHarness.dll "%CORECLR_REPO%\sandbox\%BENCHNAME%.%TEST_FILE_EXT%" --perf:runid Perf --perf:collect %COLLECTION_FLAGS% 1>"%BENCHNAME_LOG_FILE_NAME%" 2>&1
)
IF %ERRORLEVEL% NEQ 0 (
)
rem Save off the results to the root directory for recovery later in Jenkins
- call :run_cmd xcopy "Perf-%BENCHNAME%*.xml" "%CORECLR_REPO%\" || exit /b 1
- call :run_cmd xcopy "Perf-%BENCHNAME%*.etl" "%CORECLR_REPO%\" || exit /b 1
+ IF EXIST "Perf-%BENCHNAME%.xml" (
+ call :run_cmd copy "Perf-%BENCHNAME%.xml" "%CORECLR_REPO%\Perf-%BENCHNAME%-%ETW_COLLECTION%.xml" || exit /b 1
+ )
+ IF EXIST "Perf-%BENCHNAME%.etl" (
+ call :run_cmd copy "Perf-%BENCHNAME%.etl" "%CORECLR_REPO%\Perf-%BENCHNAME%-%ETW_COLLECTION%.etl" || exit /b 1
+ )
exit /b 0
shift
goto :parse_command_line_arguments
)
+ IF /I [%~1] == [-stabilityPrefix] (
+ set STABILITY_PREFIX=%~2
+ shift
+ shift
+ goto :parse_command_line_arguments
+ )
IF /I [%~1] == [-scenarioTest] (
set IS_SCENARIO_TEST=1
shift
shift
goto :parse_command_line_arguments
)
+ IF /I [%~1] == [-collectionflags] (
+ set COLLECTION_FLAGS=%~2
+ shift
+ shift
+ goto :parse_command_line_arguments
+ )
IF /I [%~1] == [-library] (
set TEST_FILE_EXT=dll
shift
)
exit /b 0
+ :set_collection_config
+rem ****************************************************************************
+rem Set's the config based on the providers used for collection
+rem ****************************************************************************
+ if /I [%COLLECTION_FLAGS%] == [stopwatch] (
+ set ETW_COLLECTION=Off
+ ) else (
+ set ETW_COLLECTION=On
+ )
+ exit /b 0
+
+
:set_perf_run_log
rem ****************************************************************************
rem Sets the script's output log file.
set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --config-name "%TEST_CONFIG%"
set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --config Configuration "%TEST_CONFIG%"
set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --config OS "Windows_NT"
+ set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --config Profile "%ETW_COLLECTION%"
set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --arch "%TEST_ARCHITECTURE%"
set LV_SUBMISSION_ARGS=%LV_SUBMISSION_ARGS% --machinepool "PerfSnake"
call :run_cmd py.exe "%BENCHVIEW_PATH%\submission.py" measurement.json %LV_SUBMISSION_ARGS%
rem Script's usage.
rem ****************************************************************************
set USAGE_DISPLAYED=1
- echo run-xunit-perf.cmd -testBinLoc ^<path_to_tests^> [-library] [-arch] ^<x86^|x64^> [-configuration] ^<Release^|Debug^> [-generateBenchviewData] ^<path_to_benchview_tools^> [-runtype] ^<rolling^|private^> [-scenarioTest]
+ echo run-xunit-perf.cmd -testBinLoc ^<path_to_tests^> [-library] [-arch] ^<x86^|x64^> [-configuration] ^<Release^|Debug^> [-generateBenchviewData] ^<path_to_benchview_tools^> [-runtype] ^<rolling^|private^> [-scenarioTest] [-collectionFlags] ^<default^+CacheMisses^+InstructionRetired^+BranchMispredictions^+gcapi^>
echo/
echo For the path to the tests you can pass a parent directory and the script will grovel for
echo all tests in subdirectories and run them.
echo Runtype sets the runtype that we upload to Benchview, rolling for regular runs, and private for
echo PRs.
echo -scenarioTest should be included if you are running a scenario benchmark.
+ echo -collectionFlags This is used to specify what collectoin flags get passed to the performance
+ echo harness that is doing the test running. If this is not specified we only use stopwatch.
+ echo Other flags are "default", which is the whatever the test being run specified, "CacheMisses",
+ echo "BranchMispredictions", and "InstructionsRetired".
exit /b %ERRORLEVEL%
:print_error
"type": "platform",
"version": "1.1.0"
},
- "xunit.performance.api": "1.0.0-beta-build0003"
+ "xunit.performance.api": "1.0.0-beta-build0004"
}
}
}
{
"dependencies": {
- "Microsoft.NETCore.ILAsm": "2.0.0-preview2-25303-03",
- "Microsoft.NETCore.ILDAsm": "2.0.0-preview2-25303-03",
- "Microsoft.NETCore.Jit": "2.0.0-preview2-25303-03",
- "Microsoft.NETCore.Runtime.CoreCLR": "2.0.0-preview2-25303-03",
- "Microsoft.NETCore.TestHost": "2.0.0-preview2-25303-03"
+ "Microsoft.NETCore.ILAsm": "2.0.0-preview2-25309-01",
+ "Microsoft.NETCore.ILDAsm": "2.0.0-preview2-25309-01",
+ "Microsoft.NETCore.Jit": "2.0.0-preview2-25309-01",
+ "Microsoft.NETCore.Runtime.CoreCLR": "2.0.0-preview2-25309-01",
+ "Microsoft.NETCore.TestHost": "2.0.0-preview2-25309-01"
},
"frameworks": {
"netcoreapp2.0": {
{
"dependencies": {
"Microsoft.CodeAnalysis.Compilers": "1.1.1",
- "xunit.performance.api": "1.0.0-beta-build0003",
- "xunit.performance.core": "1.0.0-beta-build0003",
- "xunit.performance.execution": "1.0.0-beta-build0003",
- "xunit.performance.metrics": "1.0.0-beta-build0003",
+ "xunit.performance.api": "1.0.0-beta-build0004",
+ "xunit.performance.core": "1.0.0-beta-build0004",
+ "xunit.performance.execution": "1.0.0-beta-build0004",
+ "xunit.performance.metrics": "1.0.0-beta-build0004",
"Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.3-alpha-experimental",
"Newtonsoft.Json": "9.0.1",
"xunit": "2.2.0-beta2-build3300",
{
"dependencies": {
"Microsoft.NETCore.App": "2.0.0-beta-001494-00",
- "System.Runtime.CompilerServices.Unsafe": "4.4.0-preview2-25303-04"
+ "System.Runtime.CompilerServices.Unsafe": "4.4.0-preview2-25309-01"
},
"frameworks": {
"netcoreapp2.0": {}
{
"dependencies": {
- "Microsoft.TargetingPack.Private.CoreCLR": "2.0.0-preview2-25303-03"
+ "Microsoft.TargetingPack.Private.CoreCLR": "2.0.0-preview2-25309-01"
},
"frameworks": {
"netcoreapp1.1": {
{
"dependencies": {
- "Microsoft.NETCore.Platforms": "2.0.0-preview2-25303-04",
- "Microsoft.Private.CoreFx.NETCoreApp": "4.4.0-preview2-25303-04",
- "System.Memory": "4.4.0-preview2-25303-04",
- "System.Runtime.CompilerServices.Unsafe": "4.4.0-preview2-25303-04",
- "System.Security.Permissions": "4.4.0-preview2-25303-04"
+ "Microsoft.NETCore.Platforms": "2.0.0-preview2-25309-01",
+ "Microsoft.Private.CoreFx.NETCoreApp": "4.4.0-preview2-25309-01",
+ "System.Memory": "4.4.0-preview2-25309-01",
+ "System.Runtime.CompilerServices.Unsafe": "4.4.0-preview2-25309-01",
+ "System.Security.Permissions": "4.4.0-preview2-25309-01"
},
"frameworks": {
"netcoreapp2.0": {
{
"dependencies": {
- "Microsoft.NETCore.Platforms": "2.0.0-preview2-25303-04",
+ "Microsoft.NETCore.Platforms": "2.0.0-preview2-25309-01",
"Microsoft.DotNet.CoreCLR.TestDependencies": "1.0.0-prerelease",
"jit-dasm": "0.0.1.4",
"cijobs": "0.0.1.2",
<OutputType>Exe</OutputType>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
- <CLRTestKind>BuildAndRun</CLRTestKind>
+ <CLRTestKind>BuildOnly</CLRTestKind>
+ <GenerateRunScript>false</GenerateRunScript>
<CLRTestPriority>1</CLRTestPriority>
- <CLRTestExecutionArguments>testmix_gc.config /maximumExecutionTime:-1</CLRTestExecutionArguments>
<DefineConstants>$(DefineConstants);STATIC;PROJECTK_BUILD</DefineConstants>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.CompilerServices;
+
+class GitHub_11343
+{
+ const int Passed = 100;
+ const int Failed = 0;
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void Test()
+ {
+ string s = null;
+ // Should throw NullReferenceException even if the result is not used
+ int unused = s.Length;
+ }
+
+ static int Main()
+ {
+ try
+ {
+ Test();
+ return Failed;
+ }
+ catch (NullReferenceException)
+ {
+ return Passed;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <PropertyGroup>
+ <DebugType></DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="GitHub_11343.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+ </PropertyGroup>
+</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.CompilerServices;
+
+class GitHub_11408
+{
+ const int Pass = 100;
+ const int Fail = -1;
+
+ unsafe class Program
+ {
+ static int save = 7;
+
+ static void foo(IntPtr x)
+ {
+ save = *(int*)x;
+ Console.WriteLine(*(int*)x);
+ }
+
+ static void bar()
+ {
+ int x = 42;
+ foo((IntPtr)(&x));
+ }
+
+ public static int Main(string[] args)
+ {
+ bar();
+
+ if (save == 42)
+ {
+ Console.WriteLine("Pass");
+ return Pass;
+ }
+ else
+ {
+ Console.WriteLine("Fail");
+ return Fail;
+ }
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <PropertyGroup>
+ <DebugType></DebugType>
+ <Optimize>True</Optimize>
+ <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="GitHub_11408.cs" />
+ </ItemGroup>
+ <PropertyGroup>
+ <CLRTestBatchPreCommands><![CDATA[
+$(CLRTestBatchPreCommands)
+set COMPlus_TailcallStress=1
+]]></CLRTestBatchPreCommands>
+ <BashCLRTestPreCommands><![CDATA[
+$(BashCLRTestPreCommands)
+export COMPlus_TailcallStress=1
+]]></BashCLRTestPreCommands>
+ </PropertyGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+ </PropertyGroup>
+</Project>
{
"dependencies": {
"Microsoft.CodeAnalysis.Compilers": "1.1.1",
- "xunit.performance.api": "1.0.0-beta-build0003",
- "xunit.performance.core": "1.0.0-beta-build0003",
- "xunit.performance.execution": "1.0.0-beta-build0003",
- "xunit.performance.metrics": "1.0.0-beta-build0003",
+ "xunit.performance.api": "1.0.0-beta-build0004",
+ "xunit.performance.core": "1.0.0-beta-build0004",
+ "xunit.performance.execution": "1.0.0-beta-build0004",
+ "xunit.performance.metrics": "1.0.0-beta-build0004",
"Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.3-alpha-experimental",
- "Microsoft.NETCore.Platforms": "2.0.0-preview2-25303-04",
+ "Microsoft.NETCore.Platforms": "2.0.0-preview2-25309-01",
"System.Console": "4.4.0-beta-24913-02",
"System.Dynamic.Runtime": "4.4.0-beta-24913-02",
"System.Linq": "4.4.0-beta-24913-02",
"System.IO.FileSystem": "4.4.0-beta-24913-02",
- "System.Numerics.Vectors": "4.4.0-preview2-25303-04",
+ "System.Numerics.Vectors": "4.4.0-preview2-25309-01",
"System.Reflection": "4.4.0-beta-24913-02",
"System.Reflection.Extensions": "4.4.0-beta-24913-02",
- "System.Reflection.TypeExtensions": "4.4.0-preview2-25303-04",
+ "System.Reflection.TypeExtensions": "4.4.0-preview2-25309-01",
"System.Runtime": "4.4.0-beta-24913-02",
"System.Runtime.Extensions": "4.4.0-beta-24913-02",
"System.Runtime.Numerics": "4.4.0-beta-24913-02",
{
"dependencies": {
- "xunit.performance.api": "1.0.0-beta-build0003",
- "xunit.performance.core": "1.0.0-beta-build0003",
- "xunit.performance.execution": "1.0.0-beta-build0003",
- "xunit.performance.metrics": "1.0.0-beta-build0003",
+ "xunit.performance.api": "1.0.0-beta-build0004",
+ "xunit.performance.core": "1.0.0-beta-build0004",
+ "xunit.performance.execution": "1.0.0-beta-build0004",
+ "xunit.performance.metrics": "1.0.0-beta-build0004",
"Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.3-alpha-experimental",
- "Microsoft.NETCore.Platforms": "2.0.0-preview2-25303-04",
+ "Microsoft.NETCore.Platforms": "2.0.0-preview2-25309-01",
"Newtonsoft.Json": "7.0.1",
"System.Console": "4.4.0-beta-24913-02",
"System.IO": "4.4.0-beta-24913-02",
"System.Dynamic.Runtime": "4.4.0-beta-24913-02",
"System.Reflection": "4.4.0-beta-24913-02",
"System.Reflection.Extensions": "4.4.0-beta-24913-02",
- "System.Reflection.TypeExtensions": "4.4.0-preview2-25303-04",
+ "System.Reflection.TypeExtensions": "4.4.0-preview2-25309-01",
"System.Runtime": "4.4.0-beta-24913-02",
"System.Runtime.Serialization.Json": "4.4.0-beta-24913-02",
"System.Runtime.Serialization.Primitives": "4.4.0-beta-24913-02",
{
"dependencies": {
- "xunit.performance.api": "1.0.0-beta-build0003",
- "xunit.performance.core": "1.0.0-beta-build0003",
- "xunit.performance.execution": "1.0.0-beta-build0003",
- "xunit.performance.metrics": "1.0.0-beta-build0003",
+ "xunit.performance.api": "1.0.0-beta-build0004",
+ "xunit.performance.core": "1.0.0-beta-build0004",
+ "xunit.performance.execution": "1.0.0-beta-build0004",
+ "xunit.performance.metrics": "1.0.0-beta-build0004",
"Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.3-alpha-experimental",
- "Microsoft.NETCore.Platforms": "2.0.0-preview2-25303-04",
+ "Microsoft.NETCore.Platforms": "2.0.0-preview2-25309-01",
"System.Collections.NonGeneric": "4.4.0-beta-24913-02",
"System.Console": "4.4.0-beta-24913-02",
"System.IO.FileSystem": "4.4.0-beta-24913-02",
"System.Linq": "4.4.0-beta-24913-02",
"System.Linq.Parallel": "4.4.0-beta-24913-02",
"System.Linq.Expressions": "4.4.0-beta-24913-02",
- "System.Memory": "4.4.0-preview2-25303-04",
- "System.Numerics.Vectors": "4.4.0-preview2-25303-04",
+ "System.Memory": "4.4.0-preview2-25309-01",
+ "System.Numerics.Vectors": "4.4.0-preview2-25309-01",
"System.Reflection": "4.4.0-beta-24913-02",
"System.Reflection.Extensions": "4.4.0-beta-24913-02",
- "System.Reflection.TypeExtensions": "4.4.0-preview2-25303-04",
+ "System.Reflection.TypeExtensions": "4.4.0-preview2-25309-01",
"System.Runtime": "4.4.0-beta-24913-02",
- "System.Runtime.CompilerServices.Unsafe": "4.4.0-preview2-25303-04",
+ "System.Runtime.CompilerServices.Unsafe": "4.4.0-preview2-25309-01",
"System.Runtime.Extensions": "4.4.0-beta-24913-02",
"System.Runtime.Numerics": "4.4.0-beta-24913-02",
"System.Text.RegularExpressions": "4.4.0-beta-24913-02",
{
"dependencies": {
- "Microsoft.NETCore.Platforms": "2.0.0-preview2-25303-04",
+ "Microsoft.NETCore.Platforms": "2.0.0-preview2-25309-01",
"xunit": "2.2.0-beta2-build3300",
"xunit.assert": "2.2.0-beta2-build3300",
"xunit.core": "2.2.0-beta2-build3300",
"xunit.performance.execution": "1.0.0-beta-build0003",
"xunit.performance.metrics": "1.0.0-beta-build0003",
"Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.3-alpha-experimental",
- "Microsoft.NETCore.Platforms": "2.0.0-preview2-25303-04",
+ "Microsoft.NETCore.Platforms": "2.0.0-preview2-25309-01",
"System.Collections.NonGeneric": "4.4.0-beta-24913-02",
"System.Console": "4.4.0-beta-24913-02",
"System.IO.FileSystem": "4.4.0-beta-24913-02",
"System.Linq": "4.4.0-beta-24913-02",
"System.Linq.Expressions": "4.4.0-beta-24913-02",
- "System.Numerics.Vectors": "4.4.0-preview2-25303-04",
+ "System.Numerics.Vectors": "4.4.0-preview2-25309-01",
"System.Reflection": "4.4.0-beta-24913-02",
"System.Reflection.Extensions": "4.4.0-beta-24913-02",
- "System.Reflection.TypeExtensions": "4.4.0-preview2-25303-04",
+ "System.Reflection.TypeExtensions": "4.4.0-preview2-25309-01",
"System.Runtime": "4.4.0-beta-24913-02",
"System.Runtime.Extensions": "4.4.0-beta-24913-02",
"System.Runtime.Loader": "4.0.0",
JIT/Methodical/Boxing/xlang/_odbgsin_cs_il/_odbgsin_cs_il.sh
JIT/Methodical/Boxing/xlang/_orelsin_cs_il/_orelsin_cs_il.sh
JIT/Methodical/Boxing/xlang/_relsin_cs_il/_relsin_cs_il.sh
+JIT/Regression/JitBlue/GitHub_11408/GitHub_11408/GitHub_11408.sh
Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp/MarshalStructAsLayoutExp.sh
GC/LargeMemory/Allocation/finalizertest/finalizertest.sh
GC/LargeMemory/API/gc/reregisterforfinalize/reregisterforfinalize.sh