Class: Roby::App::Debug

Inherits:
Interface::CommandLibrary show all
Defined in:
lib/roby/app/debug.rb

Overview

A command library that allows to control StackProf to profile a Roby application

Instance Attribute Summary

Attributes inherited from Interface::CommandLibrary

#app, #subcommands

Instance Method Summary collapse

Methods inherited from Interface::CommandLibrary

command, #commands, #each_subcommand, #execution_engine, #initialize, #plan, subcommand, #subcommand

Constructor Details

This class inherits a constructor from Roby::Interface::CommandLibrary

Instance Method Details

#default_path(prefix) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The filename that is used by default in #save. It is relative to #path

It is a time tag (down to the milliseconds) followed by the sampling mode and a .dump extension



88
89
90
91
92
# File 'lib/roby/app/debug.rb', line 88

def default_path(prefix)
    path = File.join(app.log_dir, 'debug')
    time = Time.now.strftime("%Y-%m-%d.%H%M%S.%3N")
    File.join(path, "#{prefix}-#{time}.dump")
end

#memdump(path: default_path('memdump'), run_gc: true) ⇒ Object

Perform a memory dump



110
111
112
113
114
115
116
117
# File 'lib/roby/app/debug.rb', line 110

def memdump(path: default_path('memdump'), run_gc: true)
    FileUtils.mkdir_p File.dirname(path)
    File.open(path, 'wb') do |io|
        GC.start(full_mark: true, immediate_sweep: true) if run_gc
        ObjectSpace.dump_all(output: io)
    end
    path
end

#stackprof_active?Boolean

Returns:

  • (Boolean)


64
65
66
# File 'lib/roby/app/debug.rb', line 64

def stackprof_active?
    !!@cycle_counter_handler
end

#stackprof_save(path: default_path('stackprof-%s')) ⇒ Object

Save the current profiling results into the path given to #start

Parameters:

  • (String)


97
98
99
100
101
102
103
104
105
106
# File 'lib/roby/app/debug.rb', line 97

def stackprof_save(path: default_path('stackprof-%s'))
    if results = StackProf.results
        path = path % [results[:mode]]
        FileUtils.mkdir_p(File.dirname(path))
        File.open(path, 'wb') do |f|
            f.write Marshal.dump(results)
        end
        path
    end
end

#stackprof_start(one_shot: false, cycles: nil, mode: :cpu, interval: nil, raw: false) ⇒ Object

Start profiling

Parameters:

  • one_shot (Boolean) (defaults to: false)

    automatically stop and save after cycles cycles, or one cycle if cycles is nil

  • cycles (Integer) (defaults to: nil)

    a number of cycles after which the profiling is stopped and saved

  • path (String)

    the path into which results should be saved

  • mode (Symbol) (defaults to: :cpu)

    one of :cpu, :wall or :object

  • interval (Integer) (defaults to: nil)

    the sampling interval in microseconds for :cpu and :wall (defaults to 1000, that is 1ms), and the sampling rate in objects allocated for :object (defaults to 1)

  • raw (Boolean) (defaults to: false)

    whether the dump should include raw samples, needed e.g. for flamegraph generation



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
# File 'lib/roby/app/debug.rb', line 23

def stackprof_start(one_shot: false, cycles: nil, mode: :cpu, interval: nil, raw: false)
    interval ||= if mode == :object then 1
                 else 1000
                 end
    StackProf.start(mode: mode, interval: interval, raw: raw)

    if one_shot && !cycles
        cycles = 1
    end

    if cycles
        remaining_cycles = cycles
        @cycle_counter_handler = execution_engine.at_cycle_begin do
            remaining_cycles -= 1
            if remaining_cycles == 0
                execution_engine.at_cycle_end(once: true) do
                    remaining_cycles = cycles
                    StackProf.stop
                    path = stackprof_save
                    app.notify "profiling", 'INFO', "results saved in #{path} after #{cycles} cycles"
                    if one_shot
                        app.notify "profiling", 'INFO', "stopped"
                        execution_engine.remove_propagation_handler(@cycle_counter_handler)
                        @cycle_counter_handler = nil
                    else
                        StackProf.start(mode: mode, interval: interval)
                    end
                end
            end
        end
        nil
    end
end

#trace_allocations(enable) ⇒ Object

Enable or disable allocation traces for #memdump



122
123
124
125
126
127
128
129
# File 'lib/roby/app/debug.rb', line 122

def trace_allocations(enable)
    if enable
        ObjectSpace.trace_object_allocations_start
    else
        ObjectSpace.trace_object_allocations_stop
        ObjectSpace.trace_object_allocations_clear
    end
end