Module: Contrast::Agent::Patching::Policy::Patcher

Extended by:
AfterLoadPatcher
Includes:
Components::Interface
Defined in:
lib/contrast/agent/patching/policy/patcher.rb

Overview

This is how we patch into our customer’s code. It provides a way to track which classes we need to patch into and, once we’ve woven, provides a map for which methods our renamed functions need to call and how.

Constant Summary collapse

PATCH_MONITOR =

Hook to only monkeypatch Contrast. This will not trigger any other functions, like rewriting or scanning. Exposed for those situations, like ActiveRecord dynamically defining functions, where only a subset of the Assess changes are needed.

Monitor.new

Class Method Summary collapse

Methods included from AfterLoadPatcher

catchup_after_load_patches

Methods included from Components::Interface

included

Class Method Details

.patchObject

Hook to install the Contrast changes needed to allow for the instrumentation of the application - this only occurs once, during startup to catchup on everything we didn’t see get loaded



53
54
55
56
57
# File 'lib/contrast/agent/patching/policy/patcher.rb', line 53

def patch
  catchup_after_load_patches
  patch_methods
  Contrast::Agent::Assess::Policy::RewriterPatch.rewrite_interpolations
end

.patch_method(mod, methods, method_policy) ⇒ Boolean

We did it, team. We found a patcher(s) that applies to the given class (or module) and the given method. Time to do some tracking.

Parameters:

  • mod (Module)

    the module in which the patch should be placed.

  • methods (Array(Symbol))

    all the instance or singleton methods in this clazz.

  • method_policy (Contrast::Agent::Patching::Policy::MethodPolicy)

    the policy that applies to the given method_name.

Returns:

  • (Boolean)

    if patched, either by this invocation or a previous, or not



108
109
110
111
112
113
114
115
116
# File 'lib/contrast/agent/patching/policy/patcher.rb', line 108

def patch_method mod, methods, method_policy
  return false unless methods&.any? # don't even build the name if there are no methods

  if Contrast::Utils::ClassUtil.prepended_method?(mod, method_policy)
    Contrast::Agent::Patching::Policy::Patch.instrument_with_prepend(mod, method_policy)
  else
    Contrast::Agent::Patching::Policy::Patch.instrument_with_alias(mod, methods, method_policy)
  end
end

.patch_methodsObject



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/contrast/agent/patching/policy/patcher.rb', line 65

def patch_methods
  PATCH_MONITOR.synchronize do
    t = Contrast::Agent::Thread.new do
      synchronized_patch_methods
    end
    # aborting on exception makes exceptions propagate.
    t.abort_on_exception = true
    t.priority = Thread.current.priority + 1
    t.join
  end
end

.patch_specific_module(mod) ⇒ Object

This method is called by TracePointHook to instrument a specific class during a require or eval of dynamic class definition



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/contrast/agent/patching/policy/patcher.rb', line 79

def patch_specific_module mod
  with_contrast_scope do
    mod_name = mod.cs__name
    return unless Contrast::Utils::ClassUtil.truly_defined?(mod_name)
    return if AGENT.skip_instrumentation?(mod_name)

    load_patches_for_module(mod_name)

    return unless all_module_names.any? { |name| name == mod_name }

    module_data = Contrast::Agent::ModuleData.new(mod, mod_name)
    patch_into_module(module_data)
    all_module_names.delete(mod_name) if status_type.get_status(mod).patched?
  rescue StandardError => e
    logger.error('Unable to patch module', e, module: mod_name)
  end
end