public void StartEvent(string eventName, EventAttribute eventAttribute) { this.eventName = eventName; this.numParams = 0; this.byteArrArgIndices = (List <int>)null; this.events.Append(" <event").Append(" value=\"").Append(eventAttribute.EventId).Append("\"").Append(" version=\"").Append(eventAttribute.Version).Append("\"").Append(" level=\"").Append(ManifestBuilder.GetLevelName(eventAttribute.Level)).Append("\"").Append(" symbol=\"").Append(eventName).Append("\""); this.WriteMessageAttrib(this.events, "event", eventName, eventAttribute.Message); if (eventAttribute.Keywords != EventKeywords.None) { this.events.Append(" keywords=\"").Append(this.GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\""); } if (eventAttribute.Opcode != EventOpcode.Info) { this.events.Append(" opcode=\"").Append(this.GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\""); } if (eventAttribute.Task != EventTask.None) { this.events.Append(" task=\"").Append(this.GetTaskName(eventAttribute.Task, eventName)).Append("\""); } if (eventAttribute.Channel == EventChannel.None) { return; } this.events.Append(" channel=\"").Append(this.GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\""); }
/// <summary> /// Returns an EventAttribute for the Completed or Faulted events for a call context. /// </summary> /// <param name="baseAttribute">The EventAttribute for the method call that should be copied.</param> /// <param name="context">The context of the call.</param> /// <param name="nextEventId">The next event ID to use if not specified by some other mechanism.</param> /// <returns>The EventAttribute for the call context.</returns> public virtual EventAttribute CopyEventAttribute(EventAttribute baseAttribute, InvocationContext context, int nextEventId) { if (baseAttribute == null) throw new ArgumentNullException("baseAttribute"); if (context == null) throw new ArgumentNullException("context"); return new EventAttribute(nextEventId) { Keywords = baseAttribute.Keywords, Level = GetEventLevelForContext(context, baseAttribute), Message = GetEventMessage(context), Opcode = baseAttribute.Opcode, Task = baseAttribute.Task, Version = baseAttribute.Version }; }
/// <summary> /// Converts an EventAttribute to a CustomAttributeBuilder so it can be assigned to a method. /// </summary> /// <param name="attribute">The attribute to copy.</param> /// <returns>A CustomAttributeBuilder that can be assigned to a method.</returns> internal static CustomAttributeBuilder ConvertEventAttributeToAttributeBuilder(EventAttribute attribute) { var propertyValues = new object[] { attribute.Keywords, attribute.Level, attribute.Message, attribute.Opcode, attribute.Task, attribute.Version }; CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder( _eventAttributeConstructor, new object[] { attribute.EventId }, _eventAttributePropertyInfo, propertyValues); return attributeBuilder; }
/// <summary> /// Emit a call to WriteEvent(param object[]). /// </summary> /// <param name="invocationContext">The InvocationContext for this call.</param> /// <param name="methodBuilder">The MethodBuilder to append to.</param> /// <param name="eventAttribute">The EventAttribute to use as values in the method.</param> /// <param name="sourceParameterTypes">The types of parameters on the source method.</param> /// <param name="targetParameterTypes">The types of the parameters on the target method.</param> private void EmitCallWriteEvent(InvocationContext invocationContext, MethodBuilder methodBuilder, EventAttribute eventAttribute, Type[] sourceParameterTypes, Type[] targetParameterTypes) { ILGenerator mIL = methodBuilder.GetILGenerator(); // if there is no event attribute, then this is a non-event, so just return silently if (eventAttribute == null) { ProxyHelper.EmitDefaultValue(mIL, methodBuilder.ReturnType); mIL.Emit(OpCodes.Ret); return; } // call write event with the parameters in an object array mIL.Emit(OpCodes.Ldarg_0); mIL.Emit(OpCodes.Ldc_I4, eventAttribute.EventId); // create a new array of the proper length to pass the values in mIL.Emit(OpCodes.Ldc_I4, targetParameterTypes.Length); mIL.Emit(OpCodes.Newarr, typeof(object)); for (int i = 0; i < sourceParameterTypes.Length; i++) { mIL.Emit(OpCodes.Dup); mIL.Emit(OpCodes.Ldc_I4, (int)i); // load the argument and box it mIL.Emit(OpCodes.Ldarg, (int)i + 1); // if the target is a value type, then we can box the source type // and the CLR will apply conversions for us // at this point, all invalid types have been converted to strings in EmitDirectProxy // and references have been dereferenced if (targetParameterTypes[i].IsValueType) { var sourceType = sourceParameterTypes[i]; if (sourceType.IsByRef) sourceType = sourceType.GetElementType(); mIL.Emit(OpCodes.Box, sourceType); } mIL.Emit(OpCodes.Stelem, typeof(object)); } // if there is a context, call the context provider and add the context parameter if (SupportsContext(invocationContext)) { // load the array and index onto the stack mIL.Emit(OpCodes.Dup); mIL.Emit(OpCodes.Ldc_I4, (int)targetParameterTypes.Length - 1); // load the context provider mIL.Emit(OpCodes.Ldsfld, _contextProviderField); // get the invocation context from the array on the provider mIL.Emit(OpCodes.Ldsfld, _invocationContextsField); mIL.Emit(OpCodes.Ldc_I4, _invocationContexts.Count); mIL.Emit(OpCodes.Ldelem, typeof(InvocationContext)); mIL.Emit(OpCodes.Callvirt, typeof(TraceContextProvider).GetMethod("ProvideContext")); _invocationContexts.Add(invocationContext); // put the result into the array mIL.Emit(OpCodes.Stelem, typeof(object)); } // prepare for write event by setting the ETW activity ID mIL.Emit(OpCodes.Call, typeof(EventActivityScope).GetMethod("PrepareForWriteEvent")); // call writeevent mIL.Emit(OpCodes.Call, _writeEvent); mIL.Emit(OpCodes.Ret); }
/// <summary> /// Emits the code to determine whether logging is enabled. /// For NonEvents, this emits the default return value to the method, and returns false here. /// </summary> /// <param name="methodBuilder">The MethodBuilder to implement.</param> /// <param name="eventAttribute">The EventAttribute.</param> /// <returns>True if events could possibly be enabled, false if this method is a NonEvent.</returns> private static bool EmitIsEnabled(MethodBuilder methodBuilder, EventAttribute eventAttribute) { /* * This method assumes that a default return value is already on the stack * * if a nonevent: * * return (top of stack) * * if an event: * * if (!IsEnabled(level, keywords) * { * return (top of stack) * } * else * ...whatever gets emitted next */ ILGenerator mIL = methodBuilder.GetILGenerator(); // if there is no event attribute, then this is a non-event, so just return silently if (eventAttribute == null) { mIL.Emit(OpCodes.Ret); return false; } // call IsEnabled with the given event level and keywords to check whether we should log mIL.Emit(OpCodes.Ldarg_0); mIL.Emit(OpCodes.Ldc_I4, (int)eventAttribute.Level); mIL.Emit(OpCodes.Ldc_I8, (long)eventAttribute.Keywords); mIL.Emit(OpCodes.Call, _isEnabled); Label enabledLabel = mIL.DefineLabel(); mIL.Emit(OpCodes.Brtrue, enabledLabel); mIL.Emit(OpCodes.Ret); mIL.MarkLabel(enabledLabel); return true; }
public void StartEvent(string eventName, EventAttribute eventAttribute) { Contract.Assert(numParams == 0); Contract.Assert(templateName == null); templateName = eventName + "Args"; numParams = 0; events.Append(" <event"). Append(" value=\"").Append(eventAttribute.EventId).Append("\""). Append(" version=\"").Append(eventAttribute.Version).Append("\""). Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\""); WriteMessageAttrib(events, "event", eventName, eventAttribute.Message); if (eventAttribute.Keywords != 0) events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\""); if (eventAttribute.Opcode != 0) events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\""); if (eventAttribute.Task != 0) events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\""); #if FEATURE_MANAGED_ETW_CHANNELS if (eventAttribute.Channel != 0) events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName)).Append("\""); #endif }
// Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event // index for two distinct events etc. Throws exceptions when it finds something wrong. private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName, EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute) { int eventArg = GetHelperCallFirstArg(method); if (eventArg >= 0 && eventAttribute.EventId != eventArg) { throw new ArgumentException(Environment.GetResourceString("EventSource_MismatchIdToWriteEvent", method.Name, eventAttribute.EventId, eventArg)); } if (eventAttribute.EventId < eventData.Length && eventData[eventAttribute.EventId].Descriptor.EventId != 0) { throw new ArgumentException(Environment.GetResourceString("EventSource_EventIdReused", method.Name, eventAttribute.EventId)); } if (eventsByName == null) eventsByName = new Dictionary<string, string>(); if (eventsByName.ContainsKey(method.Name)) throw new ArgumentException(Environment.GetResourceString("EventSource_EventNameReused", method.Name)); eventsByName[method.Name] = method.Name; }
// Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it // it is populated if we need to look up message resources private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName, EventAttribute eventAttribute, ParameterInfo[] eventParameters) { if (eventData == null || eventData.Length <= eventAttribute.EventId) { EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)]; Array.Copy(eventData, newValues, eventData.Length); eventData = newValues; } eventData[eventAttribute.EventId].Descriptor = new System.Diagnostics.Tracing.EventDescriptor( eventAttribute.EventId, eventAttribute.Version, #if FEATURE_MANAGED_ETW_CHANNELS (byte)eventAttribute.Channel, #else (byte)0, #endif (byte)eventAttribute.Level, (byte)eventAttribute.Opcode, (int)eventAttribute.Task, (long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())); eventData[eventAttribute.EventId].Name = eventName; eventData[eventAttribute.EventId].Parameters = eventParameters; eventData[eventAttribute.EventId].Message = eventAttribute.Message; }
// Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events // at run time. 'source' is the event source to place the descriptors. If it is null, // then the descriptors are not creaed, and just the manifest is generated. private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source) { MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); EventAttribute defaultEventAttribute; int eventId = 1; // The number given to an event that does not have a explicitly given ID. EventMetadata[] eventData = null; Dictionary<string, string> eventsByName = null; if (source != null) eventData = new EventMetadata[methods.Length]; // See if we have localization information. ResourceManager resources = null; EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute)); if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null) resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly); ManifestBuilder manifest = new ManifestBuilder(GetName(eventSourceType), GetGuid(eventSourceType), eventSourceDllName, resources); // Collect task, opcode, keyword and channel information #if FEATURE_MANAGED_ETW_CHANNELS foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" }) #else foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" }) #endif { Type nestedType = eventSourceType.GetNestedType(providerEnumKind); if (nestedType != null) { foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) { AddProviderEnumKind(manifest, staticField, providerEnumKind); } } } for (int i = 0; i < methods.Length; i++) { MethodInfo method = methods[i]; ParameterInfo[] args = method.GetParameters(); // Get the EventDescriptor (from the Custom attributes) EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute)); // Methods that don't return void can't be events. if (method.ReturnType != typeof(void)) { if (eventAttribute != null) throw new ArgumentException(Environment.GetResourceString("EventSource_AttributeOnNonVoid", method.Name)); continue; } if (method.IsVirtual || method.IsStatic) { continue; } if (eventAttribute == null) { // If we explictly mark the method as not being an event, then honor that. if (GetCustomAttributeHelper(method, typeof(NonEventAttribute)) != null) continue; defaultEventAttribute = new EventAttribute(eventId); eventAttribute = defaultEventAttribute; } else if (eventAttribute.EventId <= 0) throw new ArgumentException(Environment.GetResourceString("EventSource_NeedPositiveId")); else if ((ulong)eventAttribute.Keywords >= 0x0000100000000000UL) throw new ArgumentException(Environment.GetResourceString("EventSource_ReservedKeywords")); eventId++; // Auto-assign tasks, starting with the highest task number and working back if (eventAttribute.Opcode == EventOpcode.Info && eventAttribute.Task == EventTask.None) eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId); manifest.StartEvent(method.Name, eventAttribute); for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++) { // If the first parameter is 'RelatedActivityId' then skip it. if (fieldIdx == 0 && args[fieldIdx].ParameterType == typeof(Guid) && string.Compare(args[fieldIdx].Name, "RelatedActivityId", StringComparison.OrdinalIgnoreCase) == 0) continue; manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name); } manifest.EndEvent(); if (source != null) { // Do checking for user errors (optional, but nto a big deal so we do it). DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute); AddEventDescriptor(ref eventData, method.Name, eventAttribute, args); } } if (source != null) { TrimEventDescriptors(ref eventData); source.m_eventData = eventData; // officaly initialize it. We do this at most once (it is racy otherwise). } return manifest.CreateManifest(); }
/// <summary> /// Gets the appropriate EventLevel for the call context. /// </summary> /// <param name="context">The context of the call.</param> /// <param name="baseAttribute">The base attribute to copy if there are no additional attributes.</param> /// <returns>The EventLevel for the call context.</returns> protected virtual EventLevel GetEventLevelForContext(InvocationContext context, EventAttribute baseAttribute) { if (context == null) throw new ArgumentNullException("context"); // for faulted methods, allow the EventExceptionAttribute to override the event level if (context.ContextType == InvocationContextTypes.MethodFaulted) { var attribute = context.MethodInfo.GetCustomAttribute<EventExceptionAttribute>(); if (attribute != null) return attribute.Level; attribute = context.MethodInfo.DeclaringType.GetCustomAttribute<EventExceptionAttribute>(); if (attribute != null) return attribute.Level; return ExceptionEventLevel; } // check for an attribute on the type var implementationAttribute = context.MethodInfo.DeclaringType.GetCustomAttribute<EventSourceImplementationAttribute>(); if (implementationAttribute != null && implementationAttribute.Level.HasValue) return implementationAttribute.Level.Value; if (baseAttribute != null) return baseAttribute.Level; return EventLevel; }
private static void ValidateEventMessage(EventAttribute eventAttribute, MethodInfo method, List<ParameterMapping> parameters) { if (eventAttribute == null) return; var message = eventAttribute.Message; if (message == null) return; object[] defaultValues = parameters.Select(p => GetDefaultStringFormatValue(p.TargetType)).ToArray(); try { string.Format(CultureInfo.InvariantCulture, message, defaultValues); } catch (FormatException e) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid string format in Log Method {0}", method.Name), e); } }
/// <summary> /// Emit a call to WriteEvent(param object[]). /// </summary> /// <param name="invocationContext">The InvocationContext for this call.</param> /// <param name="methodBuilder">The MethodBuilder to append to.</param> /// <param name="eventAttribute">The EventAttribute to use as values in the method.</param> /// <param name="parameterMapping">The mapping of the parameters.</param> private void EmitCallWriteEvent(InvocationContext invocationContext, MethodBuilder methodBuilder, EventAttribute eventAttribute, List<ParameterMapping> parameterMapping) { ILGenerator mIL = methodBuilder.GetILGenerator(); // if there is no event attribute, then this is a non-event, so just return silently if (eventAttribute == null) { ProxyHelper.EmitDefaultValue(mIL, methodBuilder.ReturnType); mIL.Emit(OpCodes.Ret); return; } // call write event with the parameters in an object array mIL.Emit(OpCodes.Ldarg_0); mIL.Emit(OpCodes.Ldc_I4, eventAttribute.EventId); // create a new array of the proper length to pass the values in if (parameterMapping.Count > 0) { mIL.Emit(OpCodes.Ldc_I4, parameterMapping.Count); mIL.Emit(OpCodes.Newarr, typeof(object)); } for (int i = 0; i < parameterMapping.Count; i++) { mIL.Emit(OpCodes.Dup); mIL.Emit(OpCodes.Ldc_I4, (int)i); var parameter = parameterMapping[i]; if (parameter.HasSource) { // load the argument and box it mIL.Emit(OpCodes.Ldarg, i + 1); // if the target is a value type, then we can box the source type // and the CLR will apply conversions for us // at this point, all invalid types have been converted to strings in EmitDirectProxy // and references have been dereferenced if (parameter.CleanTargetType.IsValueType) { var sourceType = parameter.SourceType; if (sourceType.IsByRef) sourceType = sourceType.GetElementType(); mIL.Emit(OpCodes.Box, sourceType); } } else { // there is no source, so get the context from the context provider // load the context provider mIL.Emit(OpCodes.Ldsfld, _contextProviderField); // get the invocation context from the array on the provider mIL.Emit(OpCodes.Ldsfld, _invocationContextsField); mIL.Emit(OpCodes.Ldc_I4, _invocationContexts.Count); mIL.Emit(OpCodes.Ldelem, typeof(InvocationContext)); mIL.Emit(OpCodes.Callvirt, typeof(TraceContextProvider).GetMethod("ProvideContext")); _invocationContexts.Add(invocationContext); } mIL.Emit(OpCodes.Stelem, typeof(object)); } // prepare for write event by setting the ETW activity ID mIL.Emit(OpCodes.Call, typeof(EventActivityScope).GetMethod("PrepareForWriteEvent")); // call writeevent mIL.Emit(OpCodes.Call, parameterMapping.Count == 0 ? _writeEventNoParams : _writeEvent); mIL.Emit(OpCodes.Ret); }