Class: PuppetDebugServer::DebugSession::BreakPoints

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet-debugserver/debug_session/break_points.rb

Overview

Manages storing and validating breakpoints for the Debug Session.

Instance Method Summary collapse

Constructor Details

#initialize(debug_session) ⇒ BreakPoints

Returns a new instance of BreakPoints.

Parameters:



8
9
10
11
12
13
14
# File 'lib/puppet-debugserver/debug_session/break_points.rb', line 8

def initialize(debug_session)
  @debug_session = debug_session

  @breakpoint_mutex = Mutex.new
  @source_breakpoints = {}
  @function_breakpoints = []
end

Instance Method Details

#function_breakpoint_namesArray<String>

Returns all of the function names that should break on.

Returns:

  • (Array<String>)

    All of the function names that the debugger should break on



113
114
115
116
117
118
# File 'lib/puppet-debugserver/debug_session/break_points.rb', line 113

def function_breakpoint_names
  result = @function_breakpoints.map(&:name)
  # Also add the debug::break function which mimics puppet-debug behaviour
  # https://github.com/nwops/puppet-debug#usage
  result << 'debug::break'
end

#line_breakpoints(file_path) ⇒ Array<Integer>

Returns all of the line breakpoints for a given file.

Parameters:

  • file_path (String)

    Absolute path to the file.

Returns:

  • (Array<Integer>)

    All of the line breakpoints. Returns empty array if there no breakpoints.



104
105
106
107
108
# File 'lib/puppet-debugserver/debug_session/break_points.rb', line 104

def line_breakpoints(file_path)
  return [] if @source_breakpoints[canonical_file_path(file_path)].nil?

  @source_breakpoints[canonical_file_path(file_path)].map(&:line)
end

#process_set_breakpoints_request!(arguments) ⇒ Array<DSP::Breakpoint>

TODO:

Do we care about Breakpoint.Id? That seems to be only required for activating/deactivating breakpoints dynamically.

Takes the arguments for the setBreakpoints request and then validates that the breakpoints requested are valid and exist.

Parameters:

Returns:

  • (Array<DSP::Breakpoint>)

    All of the breakpoints, in the same order as arguments, with validation set correctly.



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
# File 'lib/puppet-debugserver/debug_session/break_points.rb', line 22

def process_set_breakpoints_request!(arguments)
  file_path = File.expand_path(arguments.source.path) # Rub-ify the filepath. Important on Windows platforms.
  file_contents = {}

  if File.exist?(file_path)
    # Open the file and extact the lines we need
    line_list = arguments.breakpoints.map(&:line) # These are 1-based line numbers

    begin
      # TODO: This could be slow on big files....
      IO.foreach(file_path, mode: 'rb', encoding: 'UTF-8').each_with_index do |item, index|
        # index here zero-based whereas we want one-based indexing
        file_contents[index + 1] = item if line_list.include?(index + 1)
      end
    rescue StandardError => e
      PuppetDebugServer.log_message(:error, "Error reading file #{arguments.source.path} for source breakpoints: #{e}")
    end
  else
    PuppetDebugServer.log_message(:debug, "Unable to set source breakpoints for non-existant file #{arguments.source.path}")
  end

  # Create the initial list of breakpoint responses
  break_points = arguments.breakpoints.map do
    DSP::Breakpoint.new.from_h!(
      'verified' => false,
      'source' => arguments.source.to_h
    )
  end

  # The internal list of break points only cares about valid breakpoints
  @breakpoint_mutex.synchronize { @source_breakpoints[canonical_file_path(file_path)] = [] }
  # Verify that each breakpoints is valid
  arguments.breakpoints.each_with_index do |sbp, bp_index|
    line_text = file_contents[sbp.line]
    bp = break_points[bp_index]

    if line_text.nil?
      bp.message = 'Line does not exist'
      next
    end

    bp.line = sbp.line

    # Strip whitespace
    line_text.strip!
    # Strip block comments i.e. `  # something`
    line_text = line_text.partition('#')[0]

    if line_text.empty?
      bp.message = 'Line is blank'
    else
      bp.verified = true
      @breakpoint_mutex.synchronize { @source_breakpoints[canonical_file_path(file_path)] << bp }
    end
  end

  break_points
end

#process_set_function_breakpoints_request!(arguments) ⇒ Array<DSP::Breakpoint>

TODO:

Do we care about Breakpoint.Id? That seems to be only required for activating/deactivating breakpoints dynamically.

Takes the arguments for the setFunctionBreakpoints request and then validates that the breakpoints requested are valid.

Parameters:

Returns:

  • (Array<DSP::Breakpoint>)

    All of the breakpoints, in the same order as arguments, with validation set correctly.



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/puppet-debugserver/debug_session/break_points.rb', line 86

def process_set_function_breakpoints_request!(arguments)
  # Update this internal list of active breakpoints
  @breakpoint_mutex.synchronize do
    @function_breakpoints = arguments.breakpoints
  end

  # All Function breakpoints are considered valid
  arguments.breakpoints.map do
    DSP::Breakpoint.new.from_h!(
      'verified' => true
    )
  end
end