1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Debugger
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.SymbolStore;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime;
using System.Security;
using System.Security.Permissions;
using System.Activities.Debugger.Symbol;
// Manager for supporting debugging a state machine.
// The general usage is to call:
// - DefineState() for each state
// - Bake() once you've defined all the states you need to enter.
// - EnterState() / LeaveState() for each state.
// You can Define new states and bake them, such as if the script loads a new file.
// Baking is expensive, so it's best to define as many states in each batch.
[DebuggerNonUserCode]
// This class need not serialized.
[Fx.Tag.XamlVisible(false)]
public sealed class StateManager : IDisposable
{
static readonly Guid WorkflowLanguageGuid = new Guid("1F1149BB-9732-4EB8-9ED4-FA738768919C");
static readonly LocalsItemDescription[] debugInfoDescriptions = new LocalsItemDescription[] {
new LocalsItemDescription("debugInfo", typeof(DebugInfo))
};
static Type threadWorkerControllerType = typeof(ThreadWorkerController);
static MethodInfo islandWorkerMethodInfo = threadWorkerControllerType.GetMethod("IslandWorker", BindingFlags.Static | BindingFlags.Public);
internal const string MethodWithPrimingPrefix = "_";
List<LogicalThread> threads;
DynamicModuleManager dynamicModuleManager;
// Don't expose this, because that would expose the setters. Changing the properties
// after baking types has undefined semantics and would be confusing to the user.
Properties properties;
bool debugStartedAtRoot;
// Simple default constructor.
internal StateManager()
: this(new Properties(), true, null)
{
}
// Constructor.
// Properties must be set at creation time.
internal StateManager(Properties properties, bool debugStartedAtRoot, DynamicModuleManager dynamicModuleManager)
{
this.properties = properties;
this.debugStartedAtRoot = debugStartedAtRoot;
this.threads = new List<LogicalThread>();
if (dynamicModuleManager == null)
{
dynamicModuleManager = new DynamicModuleManager(this.properties.ModuleNamePrefix);
}
this.dynamicModuleManager = dynamicModuleManager;
}
internal Properties ManagerProperties
{
get { return this.properties; }
}
internal bool IsPriming
{
get;
set;
}
// Whether debugging is started at the root workflow (contrast to attaching in the middle
// of a running workflow.
internal bool DebugStartedAtRoot
{
get
{
return this.debugStartedAtRoot;
}
}
// Declare a new state associated with the given source location.
// States should have disjoint source locations.
// location is Source location associated with this state.
// This returns a state object, which can be passed to EnterState.
internal State DefineState(SourceLocation location)
{
return DefineState(location, string.Empty, null, 0);
}
internal State DefineState(SourceLocation location, string name)
{
return DefineState(location, name, null, 0);
}
internal State DefineState(SourceLocation location, string name, LocalsItemDescription[] earlyLocals, int numberOfEarlyLocals)
{
return this.dynamicModuleManager.DefineState(location, name, earlyLocals, numberOfEarlyLocals);
}
internal State DefineStateWithDebugInfo(SourceLocation location, string name)
{
return DefineState(location, name, debugInfoDescriptions, debugInfoDescriptions.Length);
}
// Bake all states using the default type namespace.
// States must be baked before calling EnterState().
internal void Bake()
{
Bake(this.properties.TypeNamePrefix, null);
}
// Bake all newly defined states. States must be baked before calling EnterState().
// typeName is the type name that the islands are contained in. This
// may show up on the callstack. If this is not unique, it will be appended with a unique
// identifier.
internal void Bake(string typeName, Dictionary<string, byte[]> checksumCache)
{
this.dynamicModuleManager.Bake(typeName, this.properties.TypeNamePrefix, checksumCache);
}
internal int CreateLogicalThread(string threadName)
{
int threadId = -1;
// Reuse thread if exists
// Start from 1 since main thread never disposed earlier.
for (int i = 1; i < this.threads.Count; ++i)
{
if (this.threads[i] == null)
{
this.threads[i] = new LogicalThread(i, threadName, this);
threadId = i;
break;
}
}
// If can't reuse old thread.
if (threadId < 0)
{
threadId = this.threads.Count;
this.threads.Add(new LogicalThread(threadId, threadName, this));
}
return threadId;
}
// Push the state onto the virtual callstack, with no locals.
// State is the state to push onto stack.
//internal void EnterState(int threadIndex, State state)
//{
// this.EnterState(threadIndex, state, null);
//}
// Enter a state and push it onto the 'virtual callstack'.
// If the user set a a breakpoint at the source location associated with
// this state, this call will hit that breakpoint.
// Call LeaveState when the interpretter is finished with this state.
//
// State is state to enter.
// "locals" is local variables (both early-bound and late-bound) associated with this state.
// Early-bound locals match by name with the set passed into DefineState.
// Late-bound will be displayed read-only to the user in the watch window.</param>
//
// EnterState can be called reentrantly. If code calls Enter(A); Enter(B); Enter(C);
// Then on the call to Enter(C), the virtual callstack will be A-->B-->C.
// Each call to Enter() will rebuild the virtual callstack.
//
internal void EnterState(int threadIndex, State state, IDictionary<string, object> locals)
{
this.EnterState(threadIndex, new VirtualStackFrame(state, locals));
}
// Enter a state and push it onto the 'virtual callstack'.
// Stackframe describing state to enter, along with the
// locals in that state.
internal void EnterState(int threadIndex, VirtualStackFrame stackFrame)
{
Fx.Assert(threadIndex < this.threads.Count, "Index out of range for thread");
Fx.Assert(this.threads[threadIndex] != null, "LogicalThread is null");
this.threads[threadIndex].EnterState(stackFrame);
}
// Pop the state most recently pushed by EnterState.
internal void LeaveState(int threadIndex, State state)
{
Fx.Assert(threadIndex < this.threads.Count, "Index out of range for thread");
Fx.Assert(this.threads[threadIndex] != null, "LogicalThread is null");
this.threads[threadIndex].LeaveState(state);
}
// Common helper to invoke an Stack frame.
// This handles marshaling the args.
// islandArguments - arbitrary argument passed ot the islands.
// [DebuggerHidden]
internal void InvokeWorker(object islandArguments, VirtualStackFrame stackFrame)
{
State state = stackFrame.State;
if (!state.DebuggingEnabled)
{
// We need to short circuit and call IslandWorker because that is what the generated code
// would have done, if we had generated it. This causes the thread to finish up.
ThreadWorkerController.IslandWorker((ThreadWorkerController)islandArguments);
return;
}
MethodInfo methodInfo = this.dynamicModuleManager.GetIsland(state, this.IsPriming);
IDictionary<string, object> allLocals = stackFrame.Locals;
// Package up the raw arguments array.
const int numberOfBaseArguments = 2;
int numberOfEarlyLocals = state.NumberOfEarlyLocals;
object[] arguments = new object[numberOfBaseArguments + numberOfEarlyLocals]; // +1 for IslandArguments and +1 for IsPriming
arguments[0] = this.IsPriming;
arguments[1] = islandArguments;
if (numberOfEarlyLocals > 0)
{
int i = numberOfBaseArguments;
foreach (LocalsItemDescription localsItemDescription in state.EarlyLocals)
{
string name = localsItemDescription.Name;
object value;
if (allLocals.TryGetValue(name, out value))
{
// We could assert that val.GetType() is assignable to localsItemDescription.Type.
// MethodInfo invoke will check this anyways; but we could check
// it and give a better error.
}
else
{
// Local not supplied in the array! Use a default.
value = Activator.CreateInstance(localsItemDescription.Type);
}
arguments[i] = value;
i++;
}
}
methodInfo.Invoke(null, arguments);
}
// Release any unmanaged resources.
// This may not necessarily unload islands or dynamic modules that were created until the calling appdomain has exited.
public void Dispose()
{
ExitThreads();
}
internal void ExitThreads()
{
foreach (LogicalThread logicalThread in this.threads)
{
if (logicalThread != null)
{
logicalThread.Exit();
}
}
this.threads.Clear();
}
// Release any unmanaged resources.
// This may not necessarily unload islands or dynamic modules that were created until the calling appdomain has exited.
public void Exit(int threadIndex)
{
Fx.Assert(threadIndex >= 0, "Invalid thread index");
Fx.Assert(this.threads[threadIndex] != null, "Cannot dispose null LogicalThread");
LogicalThread thread = this.threads[threadIndex];
thread.Exit();
// Null the entry on the List for future reuse.
this.threads[threadIndex] = null;
}
// Property bag for Manager. These provide customization hooks.
// All properties have valid default values.
[DebuggerNonUserCode]
internal class Properties
{
public Properties() :
this("Locals", "Script", "States", "WorkflowDebuggerThread", true)
{
}
public Properties(string defaultLocalsName, string moduleNamePrefix, string typeNamePrefix, string auxiliaryThreadName, bool breakOnStartup)
{
this.DefaultLocalsName = defaultLocalsName;
this.ModuleNamePrefix = moduleNamePrefix;
this.TypeNamePrefix = typeNamePrefix;
this.AuxiliaryThreadName = auxiliaryThreadName;
this.BreakOnStartup = breakOnStartup;
}
public string DefaultLocalsName
{
get;
set;
}
// The name of the dynamic module (not including extension) that the states are emitted to.
// This may show up on the callstack.
// This is a prefix because there may be multiple modules for the islands.
public string ModuleNamePrefix
{
get;
set;
}
// Typename that states are created in.
// This is a prefix because there may be multiple Types for the islands
// (such as if islands are created lazily).
public string TypeNamePrefix
{
get;
set;
}
// If UseAuxiliaryThread is true, sets the friendly name of that thread as visible
// in the debugger's window.
public string AuxiliaryThreadName
{
get;
set;
}
// If true, the VM issues a Debugger.Break() before entering the first state.
// This can be useful for an F11 experience on startup to stop at the first state.
// If this is false, then the interpreter will run until it hits a breakpoint or some
// other stopping event.
public bool BreakOnStartup
{
get;
set;
}
}
[DebuggerNonUserCode]
class LogicalThread
{
int threadId;
Stack<VirtualStackFrame> callStack;
ThreadWorkerController controller;
public LogicalThread(int threadId, string threadName, StateManager stateManager)
{
this.threadId = threadId;
this.callStack = new Stack<VirtualStackFrame>();
this.controller = new ThreadWorkerController();
this.controller.Initialize(threadName + "." + threadId.ToString(CultureInfo.InvariantCulture), stateManager);
}
// Unwind call stack cleanly.
void UnwindCallStack()
{
while (this.callStack.Count > 0)
{ // LeaveState will do the popping.
this.LeaveState(this.callStack.Peek().State);
}
}
public void Exit()
{
this.UnwindCallStack();
this.controller.Exit();
}
// Enter a state and push it onto the 'virtual callstack'.
// Stackframe describing state to enter, along with the
// locals in that state.
public void EnterState(VirtualStackFrame stackFrame)
{
if (stackFrame != null && stackFrame.State != null)
{
this.callStack.Push(stackFrame);
this.controller.EnterState(stackFrame);
}
else
{ // signify "Uninstrumented call"
this.callStack.Push(null);
}
}
// Pop the state most recently pushed by EnterState.
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters, Justification = "Revisit for bug 36860")]
public void LeaveState(State state)
{
if (this.callStack.Count > 0)
{
VirtualStackFrame stackFrame = this.callStack.Pop();
Fx.Assert(
(state == null && stackFrame == null) ||
(stackFrame != null && stackFrame.State == state),
"Unmatched LeaveState: " +
((state == null) ? "null" : state.Name) +
" with top stack frame: " +
((stackFrame == null || stackFrame.State == null) ? "null" : stackFrame.State.Name));
if (stackFrame != null) // Matches with an uninstrumented Activity.
{
this.controller.LeaveState();
}
}
else
{
Fx.Assert("Unmatched LeaveState: " + ((state != null) ? state.Name : "null"));
}
}
}
internal class DynamicModuleManager
{
// List of all state that have been created with DefineState.
List<State> states;
Dictionary<SourceLocation, State> stateMap = new Dictionary<SourceLocation, State>();
// Index into states array of the last set of states baked.
// So Bake() will build islands for each state
// { states[x], where indexLastBaked <= x < states.Length; }
int indexLastBaked;
// Mapping from State --> MethodInfo for that state.
// This gets populated as states get baked
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic assembly.")]
[SecurityCritical]
Dictionary<State, MethodInfo> islands;
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic assembly.")]
[SecurityCritical]
Dictionary<State, MethodInfo> islandsWithPriming;
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic assembly.")]
[SecurityCritical]
ModuleBuilder dynamicModule;
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic assembly.")]
[SecurityCritical]
Dictionary<string, ISymbolDocumentWriter> sourceDocuments;
[Fx.Tag.SecurityNote(Critical = "Accesses Critical members and calling Critical method InitDynamicModule.",
Safe = "We are only creating empty dictionaries, not populating them. And we are validating the provided moduleNamePrefix in partial trust.")]
[SecuritySafeCritical]
public DynamicModuleManager(string moduleNamePrefix)
{
this.states = new List<State>();
this.islands = new Dictionary<State, MethodInfo>();
this.islandsWithPriming = new Dictionary<State, MethodInfo>();
this.sourceDocuments = new Dictionary<string, ISymbolDocumentWriter>();
if (!PartialTrustHelpers.AppDomainFullyTrusted)
{
moduleNamePrefix = State.ValidateIdentifierString(moduleNamePrefix);
}
InitDynamicModule(moduleNamePrefix);
}
public State DefineState(SourceLocation location, string name, LocalsItemDescription[] earlyLocals, int numberOfEarlyLocals)
{
State state;
lock (this)
{
if (!this.stateMap.TryGetValue(location, out state))
{
lock (this)
{
state = new State(location, name, earlyLocals, numberOfEarlyLocals);
this.stateMap.Add(location, state);
this.states.Add(state);
}
}
}
return state;
}
// Bake all newly defined states. States must be baked before calling EnterState().
// typeName is the type name that the islands are contained in. This
// may show up on the callstack. If this is not unique, it will be appended with a unique
// identifier.
[Fx.Tag.SecurityNote(Critical = "Accesses Critical members and invoking Critical methods.",
Safe = "We validating the input strings - typeName and typeNamePrefix - and the checksum values in the checksumCache.")]
[SecuritySafeCritical]
public void Bake(string typeName, string typeNamePrefix, Dictionary<string, byte[]> checksumCache)
{
// In partial trust, validate the typeName and typeNamePrefix.
if (!PartialTrustHelpers.AppDomainFullyTrusted)
{
typeName = State.ValidateIdentifierString(typeName);
typeNamePrefix = State.ValidateIdentifierString(typeNamePrefix);
if (checksumCache != null)
{
bool nullifyChecksumCache = false;
foreach (KeyValuePair<string, byte[]> kvpair in checksumCache)
{
// We use an MD5 hash for the checksum, so the byte array should be 16 elements long.
if (!SymbolHelper.ValidateChecksum(kvpair.Value))
{
nullifyChecksumCache = true;
Trace.WriteLine(SR.DebugSymbolChecksumValueInvalid);
break;
}
}
// If we found an invalid checksum, just don't use the cache.
if (nullifyChecksumCache)
{
checksumCache = null;
}
}
}
lock (this)
{
if (this.indexLastBaked < this.states.Count) // there are newly created states.
{
// Ensure typename is unique. Append a number if needed.
int suffix = 1;
while (this.dynamicModule.GetType(typeName) != null)
{
typeName = typeNamePrefix + "_" + suffix.ToString(CultureInfo.InvariantCulture);
++suffix;
}
TypeBuilder typeBuilder = this.dynamicModule.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);
for (int i = indexLastBaked; i < this.states.Count; i++)
{
// Only create the island if debugging is enabled for the state.
if (this.states[i].DebuggingEnabled)
{
MethodBuilder methodBuilder = this.CreateIsland(typeBuilder, this.states[i], false, checksumCache);
Fx.Assert(methodBuilder != null, "CreateIsland call should have succeeded");
// Always generate method with priming, for the following scenario:
// 1. Start debugging a workflow inside VS, workflow debug session 1 starts (debugStartedAtRoot = true, instrumentation is done)
// 2. Workflow persisted, workflow debug session 1 ends
// 3. Workflow continued, workflow debug session 2 starts (debugStartedAtRoot = false, instrumentation is skipped because the static dynamicModuleManager is being reused and the instrumentation is done)
// 4. PrimeCallStack is called to rebuild the call stack
// 5. NullReferenceException will be thrown if MethodInfo with prime is not available
MethodBuilder methodBuilderWithPriming = this.CreateIsland(typeBuilder, this.states[i], true, checksumCache);
Fx.Assert(methodBuilderWithPriming != null, "CreateIsland call should have succeeded");
// Save information needed to call Type.GetMethod() later.
this.states[i].CacheMethodInfo(typeBuilder, methodBuilder.Name);
}
}
// Actual baking.
typeBuilder.CreateType();
// Calling Type.GetMethod() is slow (10,000 calls can take ~1 minute).
// So defer that to later.
this.indexLastBaked = this.states.Count;
}
}
}
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic module.")]
[SecurityCritical]
internal MethodBuilder CreateMethodBuilder(TypeBuilder typeBuilder, Type typeIslandArguments, State state, bool withPriming)
{
// create the method
string methodName = (state.Name != null ? state.Name : ("Line_" + state.Location.StartLine));
if (withPriming)
{
methodName = MethodWithPrimingPrefix + methodName;
}
// Parameters to the islands:
// 1. Args
// 2. IDict of late-bound locals.
// 3 ... N. list of early bound locals.
const int numberOfBaseArguments = 2;
IEnumerable<LocalsItemDescription> earlyLocals = state.EarlyLocals;
int numberOfEarlyLocals = state.NumberOfEarlyLocals;
Type[] parameterTypes = new Type[numberOfBaseArguments + numberOfEarlyLocals];
parameterTypes[0] = typeof(bool);
parameterTypes[1] = typeIslandArguments;
if (numberOfEarlyLocals > 0)
{
int i = numberOfBaseArguments;
foreach (LocalsItemDescription localsItemDescription in earlyLocals)
{
parameterTypes[i] = localsItemDescription.Type;
i++;
}
}
Type returnType = typeof(void);
MethodBuilder methodbuilder = typeBuilder.DefineMethod(
methodName,
MethodAttributes.Static | MethodAttributes.Public,
returnType, parameterTypes);
// Need to define parameter here, otherwise EE cannot get the correct IDebugContainerField
// for debugInfo.
methodbuilder.DefineParameter(1, ParameterAttributes.None, "isPriming");
methodbuilder.DefineParameter(2, ParameterAttributes.None, "typeIslandArguments");
// Define the parameter names
// Note that we can hide implementation-specific arguments from VS by not defining parameter
// info for them. Eg., the StepInTarget argument doesn't appear to show up in VS at all.
if (numberOfEarlyLocals > 0)
{
int i = numberOfBaseArguments + 1;
foreach (LocalsItemDescription localsItemDescription in earlyLocals)
{
methodbuilder.DefineParameter(i, ParameterAttributes.None, localsItemDescription.Name);
i++;
}
}
return methodbuilder;
}
[Fx.Tag.InheritThrows(From = "GetILGenerator", FromDeclaringType = typeof(MethodBuilder))]
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic module.")]
[SecurityCritical]
MethodBuilder CreateIsland(TypeBuilder typeBuilder, State state, bool withPrimingTest, Dictionary<string, byte[]>checksumCache)
{
MethodBuilder methodbuilder = this.CreateMethodBuilder(typeBuilder, threadWorkerControllerType, state, withPrimingTest);
ILGenerator ilGenerator = methodbuilder.GetILGenerator();
const int lineHidden = 0xFeeFee; // #line hidden directive
// Island:
// void MethodName(Manager m)
// {
// .line
// nop
// call Worker(m)
// ret;
// }
SourceLocation stateLocation = state.Location;
ISymbolDocumentWriter document = this.GetSourceDocument(stateLocation.FileName, stateLocation.Checksum, checksumCache);
Label islandWorkerLabel = ilGenerator.DefineLabel();
// Hide all the opcodes before the real source line.
// This is needed for Island which is called during priming (Attach to Process):
// It should skip the line directive during priming, thus it won't break at user's
// breakpoints at the beginning during priming the callstack.
if (withPrimingTest)
{
ilGenerator.MarkSequencePoint(document, lineHidden, 1, lineHidden, 100);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Brtrue, islandWorkerLabel);
}
// Emit sequence point before the IL instructions to map it to a source location.
ilGenerator.MarkSequencePoint(document, stateLocation.StartLine, stateLocation.StartColumn, stateLocation.EndLine, stateLocation.EndColumn);
ilGenerator.Emit(OpCodes.Nop);
ilGenerator.MarkLabel(islandWorkerLabel);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.EmitCall(OpCodes.Call, islandWorkerMethodInfo, null);
ilGenerator.Emit(OpCodes.Ret);
return methodbuilder;
}
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.SecureAsserts, Justification = "The validations of the user input are done elsewhere.")]
[Fx.Tag.SecurityNote(Critical = "Because we Assert UnmanagedCode in order to be able to emit symbols.")]
[SecurityCritical]
void InitDynamicModule(string asmName)
{
// See http://blogs.msdn.com/Microsoft/archive/2005/02/03/366429.aspx for a simple example
// of debuggable reflection-emit.
Fx.Assert(dynamicModule == null, "can only be initialized once");
// create a dynamic assembly and module
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = asmName;
AssemblyBuilder assemblyBuilder;
// The temporary assembly needs to be Transparent.
ConstructorInfo transparentCtor =
typeof(SecurityTransparentAttribute).GetConstructor(
Type.EmptyTypes);
CustomAttributeBuilder transparent = new CustomAttributeBuilder(
transparentCtor,
new Object[] { });
assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run, null, true, new CustomAttributeBuilder[] { transparent });
// Mark generated code as debuggable.
// See http://blogs.msdn.com/rmbyers/archive/2005/06/26/432922.aspx for explanation.
Type debuggableAttributeType = typeof(DebuggableAttribute);
ConstructorInfo constructorInfo = debuggableAttributeType.GetConstructor(new Type[] { typeof(DebuggableAttribute.DebuggingModes) });
CustomAttributeBuilder builder = new CustomAttributeBuilder(constructorInfo, new object[] {
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.Default
});
assemblyBuilder.SetCustomAttribute(builder);
// We need UnmanagedCode permissions because we are asking for Symbols to be emitted.
// We are protecting the dynamicModule so that only Critical code modifies it.
PermissionSet unmanagedCodePermissionSet = new PermissionSet(PermissionState.None);
unmanagedCodePermissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
unmanagedCodePermissionSet.Assert();
try
{
dynamicModule = assemblyBuilder.DefineDynamicModule(asmName, true); // <-- pass 'true' to track debug info.
}
finally
{
CodeAccessPermission.RevertAssert();
}
}
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic module.",
Safe = "State validates its SecurityCritical members itself")]
[SecuritySafeCritical]
public MethodInfo GetIsland(State state, bool isPriming)
{
MethodInfo island = null;
if (isPriming)
{
lock (islandsWithPriming)
{
if (!islandsWithPriming.TryGetValue(state, out island))
{
island = state.GetMethodInfo(true);
islandsWithPriming[state] = island;
}
}
}
else
{
lock (islands)
{
if (!islands.TryGetValue(state, out island))
{
island = state.GetMethodInfo(false);
islands[state] = island;
}
}
}
return island;
}
// This method is only called from CreateIsland, which is only called from Bake.
// Bake does a "lock(this)" before calling CreateIsland, so access to the sourceDocuments
// dictionary is protected by that lock. If this changes, locking will need to be added
// to this method to protect the sourceDocuments dictionary.
[Fx.Tag.SecurityNote(Critical = "Used in generating the dynamic module.")]
[SecurityCritical]
private ISymbolDocumentWriter GetSourceDocument(string fileName, byte[] checksum, Dictionary<string, byte[]> checksumCache)
{
ISymbolDocumentWriter documentWriter;
string sourceDocKey = fileName + SymbolHelper.GetHexStringFromChecksum(checksum);
if (!this.sourceDocuments.TryGetValue(sourceDocKey, out documentWriter))
{
documentWriter =
dynamicModule.DefineDocument(
fileName,
StateManager.WorkflowLanguageGuid,
SymLanguageVendor.Microsoft,
SymDocumentType.Text);
this.sourceDocuments.Add(sourceDocKey, documentWriter);
byte[] checksumBytes;
if (checksumCache == null || !checksumCache.TryGetValue(fileName.ToUpperInvariant(), out checksumBytes))
{
checksumBytes = SymbolHelper.CalculateChecksum(fileName);
}
if (checksumBytes != null)
{
documentWriter.SetCheckSum(SymbolHelper.ChecksumProviderId, checksumBytes);
}
}
return documentWriter;
}
}
}
}
|