satyr 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Rakefile +8 -0
- data/lib/satyr.rb +176 -0
- data/test/data/ureport-1 +107 -0
- data/test/test_satyr.rb +43 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b52e512f3fb48bce3031e8d8b88ee0764fcc8f02
|
4
|
+
data.tar.gz: 85e6bef907ae38f51c744df06443402f87f61c83
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e7150a99c1df538f986fc7150f6c4ddf9b5a4fe7c5b05909211964358098d2dd12efd7780e180cecd23fa79353e84c10d8ccc08e7285c148ad26b7347130bd1
|
7
|
+
data.tar.gz: 22f78ab04653c4504f4c9b30b982c450bee87967d43871102f5460200a4635b771b6690d9e1f88c871267c43787f3c1d0e2348b03d1d3c15761994f31a34baf1
|
data/Rakefile
ADDED
data/lib/satyr.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
# Ruby bindings for libsatyr
|
4
|
+
module Satyr
|
5
|
+
|
6
|
+
# FFI wrappers for C functions. <em>Do not use this module directly</em>
|
7
|
+
module FFI
|
8
|
+
extend ::FFI::Library
|
9
|
+
|
10
|
+
ffi_lib 'libsatyr.so.3', ::FFI::Library::LIBC
|
11
|
+
|
12
|
+
enum :report_type, [ :invalid, 0,
|
13
|
+
:core,
|
14
|
+
:python,
|
15
|
+
:kerneloops,
|
16
|
+
:java,
|
17
|
+
:gdb ]
|
18
|
+
|
19
|
+
enum :duphash_flags, [ :normal, 1 << 0,
|
20
|
+
:nohash, 1 << 1,
|
21
|
+
:nonormalize, 1 << 2,
|
22
|
+
:koops_compat, 1 << 3 ]
|
23
|
+
|
24
|
+
attach_function :sr_report_from_json_text, [:string, :pointer], :pointer
|
25
|
+
attach_function :sr_report_free, [:pointer], :void
|
26
|
+
|
27
|
+
attach_function :sr_stacktrace_find_crash_thread, [:pointer], :pointer
|
28
|
+
attach_function :sr_core_stacktrace_dup, [:pointer], :pointer
|
29
|
+
attach_function :sr_python_stacktrace_dup, [:pointer], :pointer
|
30
|
+
attach_function :sr_koops_stacktrace_dup, [:pointer], :pointer
|
31
|
+
attach_function :sr_java_stacktrace_dup, [:pointer], :pointer
|
32
|
+
attach_function :sr_gdb_stacktrace_dup, [:pointer], :pointer
|
33
|
+
attach_function :sr_stacktrace_free, [:pointer], :void
|
34
|
+
|
35
|
+
attach_function :sr_thread_dup, [:pointer], :pointer
|
36
|
+
attach_function :sr_thread_get_duphash, [:pointer, :int, :string, :duphash_flags], :pointer
|
37
|
+
attach_function :sr_thread_free, [:pointer], :void
|
38
|
+
|
39
|
+
attach_function :free, [:pointer], :void
|
40
|
+
|
41
|
+
class ReportStruct < ::FFI::ManagedStruct
|
42
|
+
layout :version, :uint32,
|
43
|
+
:type, :report_type,
|
44
|
+
:reporter_name, :string,
|
45
|
+
:reporter_version, :string,
|
46
|
+
:user_root, :bool,
|
47
|
+
:user_local, :bool,
|
48
|
+
:operating_system, :pointer,
|
49
|
+
:component_name, :string,
|
50
|
+
:rpm_packages, :pointer,
|
51
|
+
:stacktrace, :pointer
|
52
|
+
|
53
|
+
def self.release(pointer)
|
54
|
+
Satyr::FFI.sr_report_free pointer
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class StacktraceStruct < ::FFI::ManagedStruct
|
59
|
+
layout :type, :report_type
|
60
|
+
|
61
|
+
def self.release(pointer)
|
62
|
+
Satyr::FFI.sr_stacktrace_free pointer
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class ThreadStruct < ::FFI::ManagedStruct
|
67
|
+
layout :type, :report_type
|
68
|
+
|
69
|
+
def self.release(pointer)
|
70
|
+
Satyr::FFI.sr_thread_free pointer
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
# Exception for errors originating in satyr
|
77
|
+
class SatyrError < StandardError
|
78
|
+
end
|
79
|
+
|
80
|
+
# Report containing all data relevant to a software problem
|
81
|
+
class Report
|
82
|
+
|
83
|
+
# Parses given JSON string to create new Report
|
84
|
+
def initialize(json)
|
85
|
+
error_msg = ::FFI::MemoryPointer.new(:pointer, 1)
|
86
|
+
pointer = Satyr::FFI.sr_report_from_json_text json.to_s, error_msg
|
87
|
+
error_msg = error_msg.read_pointer
|
88
|
+
unless error_msg.null?
|
89
|
+
message = error_msg.read_string.force_encoding('UTF-8')
|
90
|
+
Satyr::FFI.free error_msg
|
91
|
+
raise SatyrError, "Failed to parse JSON: #{message}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# from_json_text should never return NULL without setting error_msg,
|
95
|
+
# better err on the safe side though
|
96
|
+
raise SatyrError, "Failed to create stacktrace" if pointer.null?
|
97
|
+
|
98
|
+
@struct = Satyr::FFI::ReportStruct.new pointer
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns Stacktrace of the report
|
102
|
+
def stacktrace
|
103
|
+
stacktrace = @struct[:stacktrace]
|
104
|
+
return nil if stacktrace.null?
|
105
|
+
|
106
|
+
# There's no sr_stacktrace_dup in libsatyr.so.3, we've got to find out
|
107
|
+
# the type ourselves.
|
108
|
+
dup = case @struct[:type]
|
109
|
+
when :core
|
110
|
+
Satyr::FFI.sr_core_stacktrace_dup(stacktrace)
|
111
|
+
when :python
|
112
|
+
Satyr::FFI.sr_python_stacktrace_dup(stacktrace)
|
113
|
+
when :kerneloops
|
114
|
+
Satyr::FFI.sr_koops_stacktrace_dup(stacktrace)
|
115
|
+
when :java
|
116
|
+
Satyr::FFI.sr_java_stacktrace_dup(stacktrace)
|
117
|
+
when :gdb
|
118
|
+
Satyr::FFI.sr_gdb_stacktrace_dup(stacktrace)
|
119
|
+
else
|
120
|
+
raise SatyrError, "Invalid report type"
|
121
|
+
end
|
122
|
+
|
123
|
+
raise SatyrError, "Failed to obtain stacktrace" if dup.null?
|
124
|
+
|
125
|
+
Stacktrace.send(:new, dup)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Program stack trace, possibly composed of multiple threads
|
130
|
+
class Stacktrace
|
131
|
+
|
132
|
+
# Private constructor - Stacktrace objects are created in Report#stacktrace
|
133
|
+
def initialize(pointer)
|
134
|
+
raise SatyrError, "Invalid structure" if pointer.null?
|
135
|
+
@struct = Satyr::FFI::StacktraceStruct.new pointer
|
136
|
+
end
|
137
|
+
private_class_method :new
|
138
|
+
|
139
|
+
# Returns Thread that likely caused the crash that produced this stack
|
140
|
+
# trace
|
141
|
+
def find_crash_thread
|
142
|
+
pointer = Satyr::FFI.sr_stacktrace_find_crash_thread @struct.to_ptr
|
143
|
+
raise SatyrError, "Could not find crash thread" if pointer.null?
|
144
|
+
dup = Satyr::FFI.sr_thread_dup(pointer)
|
145
|
+
raise SatyrError, "Failed to duplicate thread" if dup.null?
|
146
|
+
|
147
|
+
Thread.send(:new, dup)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Thread in a stack trace containing individual stack frames
|
152
|
+
class Thread
|
153
|
+
|
154
|
+
# Private constructor - Thread objects are returned by Stacktrace methods
|
155
|
+
def initialize(pointer)
|
156
|
+
raise SatyrError, "Invalid structure" if pointer.null?
|
157
|
+
@struct = Satyr::FFI::ThreadStruct.new pointer
|
158
|
+
end
|
159
|
+
private_class_method :new
|
160
|
+
|
161
|
+
# Duplication hash for the thread
|
162
|
+
# The method takes an option hash with following keys:
|
163
|
+
# - +:frames+:: number of frames to use (default 0 means use all)
|
164
|
+
# - +:flags+:: bitwise sum of (:normal, :nohash, :nonormalize,
|
165
|
+
# :koops_compat)
|
166
|
+
# - +:prefix+:: string to be prepended in front of the text before hashing
|
167
|
+
def duphash(opts = {})
|
168
|
+
opts = {:frames => 0, :flags => :normal, :prefix => ""}.merge(opts)
|
169
|
+
str_pointer = Satyr::FFI.sr_thread_get_duphash @struct.to_ptr, opts[:frames], opts[:prefix], opts[:flags]
|
170
|
+
raise SatyrError, "Failed to compute duphash" if str_pointer.null?
|
171
|
+
hash = str_pointer.read_string.force_encoding('UTF-8')
|
172
|
+
Satyr::FFI.free str_pointer
|
173
|
+
hash
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/test/data/ureport-1
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
{
|
2
|
+
"ureport_version": 2,
|
3
|
+
|
4
|
+
"reason": "Program /usr/bin/sleep was terminated by signal 11",
|
5
|
+
|
6
|
+
"os": {
|
7
|
+
"name": "fedora",
|
8
|
+
"version": "18",
|
9
|
+
"architecture": "x86_64"
|
10
|
+
},
|
11
|
+
|
12
|
+
"problem": {
|
13
|
+
"type": "core",
|
14
|
+
|
15
|
+
"executable": "/usr/bin/sleep",
|
16
|
+
|
17
|
+
"signal": 11,
|
18
|
+
|
19
|
+
"component": "coreutils",
|
20
|
+
|
21
|
+
"user": {
|
22
|
+
"local": true,
|
23
|
+
"root": false
|
24
|
+
},
|
25
|
+
|
26
|
+
"stacktrace": [
|
27
|
+
{
|
28
|
+
"crash_thread": true,
|
29
|
+
|
30
|
+
"frames": [
|
31
|
+
{
|
32
|
+
"build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6",
|
33
|
+
"build_id_offset": 767024,
|
34
|
+
"file_name": "/lib64/libc.so.6",
|
35
|
+
"address": 251315074096,
|
36
|
+
"fingerprint": "6c1eb9626919a2a5f6a4fc4c2edc9b21b33b7354",
|
37
|
+
"function_name": "__nanosleep"
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32",
|
41
|
+
"build_id_offset": 16567,
|
42
|
+
"file_name": "/usr/bin/sleep",
|
43
|
+
"address": 4210871,
|
44
|
+
"fingerprint": "d24433b82a2c751fc580f47154823e0bed641a54",
|
45
|
+
"function_name": "close_stdout"
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32",
|
49
|
+
"build_id_offset": 16202,
|
50
|
+
"file_name": "/usr/bin/sleep",
|
51
|
+
"address": 4210506,
|
52
|
+
"fingerprint": "562719fb960d1c4dbf30c04b3cff37c82acc3d2d",
|
53
|
+
"function_name": "close_stdout"
|
54
|
+
},
|
55
|
+
{
|
56
|
+
"build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32",
|
57
|
+
"build_id_offset": 6404,
|
58
|
+
"fingerprint": "2e8fb95adafe21d035b9bcb9993810fecf4be657",
|
59
|
+
"file_name": "/usr/bin/sleep",
|
60
|
+
"address": 4200708
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6",
|
64
|
+
"build_id_offset": 137733,
|
65
|
+
"file_name": "/lib64/libc.so.6",
|
66
|
+
"address": 251314444805,
|
67
|
+
"fingerprint": "075acda5d3230e115cf7c88597eaba416bdaa6bb",
|
68
|
+
"function_name": "__libc_start_main"
|
69
|
+
}
|
70
|
+
]
|
71
|
+
}
|
72
|
+
]
|
73
|
+
},
|
74
|
+
|
75
|
+
"packages": [
|
76
|
+
{
|
77
|
+
"name": "coreutils",
|
78
|
+
"epoch": 0,
|
79
|
+
"version": "8.17",
|
80
|
+
"architecture": "x86_64",
|
81
|
+
"package_role": "affected",
|
82
|
+
"release": "8.fc18",
|
83
|
+
"install_time": 1371464601
|
84
|
+
},
|
85
|
+
{
|
86
|
+
"name": "glibc",
|
87
|
+
"epoch": 0,
|
88
|
+
"version": "2.16",
|
89
|
+
"architecture": "x86_64",
|
90
|
+
"release": "31.fc18",
|
91
|
+
"install_time": 1371464176
|
92
|
+
},
|
93
|
+
{
|
94
|
+
"name": "glibc-common",
|
95
|
+
"epoch": 0,
|
96
|
+
"version": "2.16",
|
97
|
+
"architecture": "x86_64",
|
98
|
+
"release": "31.fc18",
|
99
|
+
"install_time": 1371464184
|
100
|
+
}
|
101
|
+
],
|
102
|
+
|
103
|
+
"reporter": {
|
104
|
+
"version": "0.3",
|
105
|
+
"name": "satyr"
|
106
|
+
}
|
107
|
+
}
|
data/test/test_satyr.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'satyr'
|
3
|
+
|
4
|
+
class SatyrTest < Test::Unit::TestCase
|
5
|
+
def test_core_duphash
|
6
|
+
path = 'test/data/ureport-1'
|
7
|
+
json = IO.read(path)
|
8
|
+
|
9
|
+
report = Satyr::Report.new json
|
10
|
+
stacktrace = report.stacktrace
|
11
|
+
thread = stacktrace.find_crash_thread
|
12
|
+
|
13
|
+
assert_equal 'b9a440a68b6e0f33f6cd989e114d6c4093964e10', thread.duphash
|
14
|
+
|
15
|
+
expected_out = "Thread\n5f6632d75fd027f5b7b410787f3f06c6bf73eee6+0xbb430\n"
|
16
|
+
assert_equal expected_out, thread.duphash(frames: 1, flags: :nohash)
|
17
|
+
|
18
|
+
expected_out = "a" + expected_out
|
19
|
+
assert_equal expected_out, thread.duphash(frames: 1, flags: :nohash, prefix: "a")
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_invalid_report
|
23
|
+
assert_raise Satyr::SatyrError do
|
24
|
+
report = Satyr::Report.new ""
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
report = Satyr::Report.new "}"
|
29
|
+
rescue Satyr::SatyrError => e
|
30
|
+
assert_match /Failed to parse JSON:.*}.*/, e.message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_private_constructors
|
35
|
+
assert_raise NoMethodError do
|
36
|
+
s = Satyr::Stacktrace.new nil
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_raise NoMethodError do
|
40
|
+
s = Satyr::Thread.new nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: satyr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Milata
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Ruby bindings for satyr, library for working with the uReport problem
|
28
|
+
report format.
|
29
|
+
email: mmilata@redhat.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/satyr.rb
|
35
|
+
- Rakefile
|
36
|
+
- test/test_satyr.rb
|
37
|
+
- test/data/ureport-1
|
38
|
+
homepage: http://github.com/abrt/satyr
|
39
|
+
licenses:
|
40
|
+
- GPLv2
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 2.1.11
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Parse uReport bug report format
|
62
|
+
test_files:
|
63
|
+
- test/test_satyr.rb
|
64
|
+
- test/data/ureport-1
|