Коммит
c65a2029b9
|
@ -118,7 +118,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cpp_demangle"
|
||||
version = "0.2.13"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -196,11 +196,12 @@ name = "fix-stacks"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"goblin 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
|
||||
"symbolic-debuginfo 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
|
||||
"symbolic-demangle 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
|
||||
"symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
|
||||
"symbolic-debuginfo 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
|
||||
"symbolic-demangle 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -253,12 +254,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "goblin"
|
||||
version = "0.0.23"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/jan-auer/goblin?rev=ee8b997792d37cfb35f8656c028543797425137e#ee8b997792d37cfb35f8656c028543797425137e"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "goblin"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -375,7 +386,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pdb"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/jan-auer/pdb?rev=79112ae2fd208fcb5c8e565dd148f4b55c3e58e3#79112ae2fd208fcb5c8e565dd148f4b55c3e58e3"
|
||||
source = "git+https://github.com/jan-auer/pdb?rev=6518a17aff69ed26375cf3358632abbd32073931#6518a17aff69ed26375cf3358632abbd32073931"
|
||||
dependencies = [
|
||||
"fallible-iterator 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -620,6 +631,14 @@ dependencies = [
|
|||
"scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll_derive"
|
||||
version = "0.9.5"
|
||||
|
@ -630,6 +649,16 @@ dependencies = [
|
|||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll_derive"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
|
@ -698,7 +727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
[[package]]
|
||||
name = "symbolic-common"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf#d217a9340df3bbd373323b732880a95e6de353bf"
|
||||
source = "git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63#7d4ca78958aab62104a9fc18bb17cdba616d7a63"
|
||||
dependencies = [
|
||||
"debugid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -710,38 +739,38 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "symbolic-debuginfo"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf#d217a9340df3bbd373323b732880a95e6de353bf"
|
||||
source = "git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63#7d4ca78958aab62104a9fc18bb17cdba616d7a63"
|
||||
dependencies = [
|
||||
"dmsort 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gimli 0.19.0 (git+https://github.com/jan-auer/gimli?rev=bf8ea0f0079505681c360d3a55d0ac1e9b498df3)",
|
||||
"goblin 0.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"goblin 0.1.1 (git+https://github.com/jan-auer/goblin?rev=ee8b997792d37cfb35f8656c028543797425137e)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=79112ae2fd208fcb5c8e565dd148f4b55c3e58e3)",
|
||||
"pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=6518a17aff69ed26375cf3358632abbd32073931)",
|
||||
"pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
|
||||
"symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
|
||||
"zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symbolic-demangle"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf#d217a9340df3bbd373323b732880a95e6de353bf"
|
||||
source = "git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63#7d4ca78958aab62104a9fc18bb17cdba616d7a63"
|
||||
dependencies = [
|
||||
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cpp_demangle 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"msvc-demangler 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
|
||||
"symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -866,7 +895,7 @@ dependencies = [
|
|||
"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum cpp_demangle 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1d355451c6fb0dd4142db91e8dbd020d0d5466393957e771032bb9bf779504b4"
|
||||
"checksum cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4115af6f575a7bc82c613e9e0ed7cc36a5e4fc3a8b54920dc0c820823a31a0d6"
|
||||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
"checksum debugid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "751dad1347b163aa77262232129c7ac46e2810485c9b095ac9f7caf200e97df4"
|
||||
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
|
@ -882,7 +911,8 @@ dependencies = [
|
|||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum gimli 0.19.0 (git+https://github.com/jan-auer/gimli?rev=bf8ea0f0079505681c360d3a55d0ac1e9b498df3)" = "<none>"
|
||||
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
"checksum goblin 0.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "ac56b4753b6b8c2e052ca30717e5a09acf1b02a2c1681bf3d883bd660e5d22bd"
|
||||
"checksum goblin 0.1.1 (git+https://github.com/jan-auer/goblin?rev=ee8b997792d37cfb35f8656c028543797425137e)" = "<none>"
|
||||
"checksum goblin 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88a79ef1f0dad46fd78075b6f80f92d97710eddf87b3e18a15a66761e8942672"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||
|
@ -899,7 +929,7 @@ dependencies = [
|
|||
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7"
|
||||
"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c"
|
||||
"checksum pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=79112ae2fd208fcb5c8e565dd148f4b55c3e58e3)" = "<none>"
|
||||
"checksum pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=6518a17aff69ed26375cf3358632abbd32073931)" = "<none>"
|
||||
"checksum pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a"
|
||||
"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
|
||||
"checksum pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0"
|
||||
|
@ -928,7 +958,9 @@ dependencies = [
|
|||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||
"checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1"
|
||||
"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383"
|
||||
"checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28"
|
||||
"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
@ -938,9 +970,9 @@ dependencies = [
|
|||
"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
|
||||
"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
|
||||
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
"checksum symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)" = "<none>"
|
||||
"checksum symbolic-debuginfo 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)" = "<none>"
|
||||
"checksum symbolic-demangle 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)" = "<none>"
|
||||
"checksum symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)" = "<none>"
|
||||
"checksum symbolic-debuginfo 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)" = "<none>"
|
||||
"checksum symbolic-demangle 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)" = "<none>"
|
||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92"
|
||||
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -7,6 +7,7 @@ license = "MIT/Apache-2.0"
|
|||
|
||||
[dependencies]
|
||||
fxhash = "0.2.1"
|
||||
goblin = "0.1.2"
|
||||
regex = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
|
@ -14,6 +15,12 @@ serde_json = "1.0"
|
|||
# writing, 6.1.4 is the latest version of `symbolic` on crates.io, but this dev
|
||||
# version of 7.0.0 fixes some API botches (`ObjectDebugSession::functions()`
|
||||
# being private). Once 7.0.0 is released, we can switch to it.
|
||||
symbolic-common = { git = "https://github.com/getsentry/symbolic/", rev = "d217a9340df3bbd373323b732880a95e6de353bf" }
|
||||
symbolic-debuginfo = { git = "https://github.com/getsentry/symbolic/", rev = "d217a9340df3bbd373323b732880a95e6de353bf" }
|
||||
symbolic-demangle = { git = "https://github.com/getsentry/symbolic/", rev = "d217a9340df3bbd373323b732880a95e6de353bf" }
|
||||
#
|
||||
# FIXME(https://github.com/mozilla/fix-stacks/issues/11): if/when my
|
||||
# symbolic-debuginfo changes land
|
||||
# (https://github.com/getsentry/symbolic/pull/173), change these to point again
|
||||
# to the `getsentry` repo.
|
||||
#
|
||||
symbolic-common = { git = "https://github.com/nnethercote/symbolic/", rev = "7d4ca78958aab62104a9fc18bb17cdba616d7a63" }
|
||||
symbolic-debuginfo = { git = "https://github.com/nnethercote/symbolic/", rev = "7d4ca78958aab62104a9fc18bb17cdba616d7a63" }
|
||||
symbolic-demangle = { git = "https://github.com/nnethercote/symbolic/", rev = "7d4ca78958aab62104a9fc18bb17cdba616d7a63" }
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
This program post-processes ("fixes") the stack frames produced by
|
||||
`MozFormatCodeAddress()`, which often lack one or more of: function name, file
|
||||
name, line number. It relies on the `symbolic` crate to read debug info from
|
||||
files.
|
||||
name, line number. It relies on the `symbolic` and `goblin` crates to read
|
||||
debug info from files.
|
||||
|
||||
It reads from standard input and writes to standard output. Lines matching the
|
||||
special stack frame format are modified appropriately. For example, a line
|
||||
|
@ -24,10 +24,9 @@ the stack frames and the build files. Furthermore, the build files must not
|
|||
have changed since the stack frames were produced. Otherwise, source locations
|
||||
in the output may be missing or incorrect.
|
||||
|
||||
# Shortcomings
|
||||
`fix-stacks` works on Linux, Windows, and Mac.
|
||||
|
||||
`fix-stacks` works on Linux and Windows. We aim to eventually support
|
||||
[Mac](https://github.com/mozilla/fix-stacks/issues/3) and possibly Android.
|
||||
# Shortcomings
|
||||
|
||||
On Linux, use with debuginfo sections in separate files is untested and
|
||||
probably does not work.
|
||||
|
|
362
src/main.rs
362
src/main.rs
|
@ -3,14 +3,15 @@
|
|||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
use fxhash::FxHashMap;
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use goblin::{archive, mach};
|
||||
use regex::Regex;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{self, BufRead, Write};
|
||||
use symbolic_common::Name;
|
||||
use symbolic_debuginfo::{FileFormat, Function, Object, ObjectDebugSession};
|
||||
use symbolic_common::{Arch, Name};
|
||||
use symbolic_debuginfo::{Archive, FileFormat, Function, Object, ObjectDebugSession};
|
||||
use symbolic_demangle::{Demangle, DemangleFormat, DemangleOptions};
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -57,6 +58,18 @@ enum JsonEscaping {
|
|||
Yes,
|
||||
}
|
||||
|
||||
fn format_address(address: u64, offset: i64) -> String {
|
||||
if offset == 0 {
|
||||
format!("0x{:x}", address)
|
||||
} else {
|
||||
format!(
|
||||
"0x{:x} -> 0x{:x}",
|
||||
address,
|
||||
(address as i64 + offset) as u64
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug info for a single line.
|
||||
struct LineInfo {
|
||||
address: u64,
|
||||
|
@ -67,17 +80,17 @@ struct LineInfo {
|
|||
}
|
||||
|
||||
impl LineInfo {
|
||||
fn new(interner: &mut Interner, line: symbolic_debuginfo::LineInfo) -> LineInfo {
|
||||
fn new(interner: &mut Interner, line: symbolic_debuginfo::LineInfo, offset: i64) -> LineInfo {
|
||||
if PRINT_FUNCS_AND_LINES {
|
||||
eprintln!(
|
||||
"LINE 0x{:x} line={} file={}",
|
||||
line.address,
|
||||
"LINE {} line={} file={}",
|
||||
format_address(line.address, offset),
|
||||
line.line,
|
||||
line.file.path_str()
|
||||
);
|
||||
}
|
||||
LineInfo {
|
||||
address: line.address,
|
||||
address: (line.address as i64 + offset) as u64,
|
||||
line: line.line,
|
||||
path: interner.intern(line.file.path_str()),
|
||||
}
|
||||
|
@ -98,23 +111,23 @@ struct FuncInfo {
|
|||
}
|
||||
|
||||
impl FuncInfo {
|
||||
fn new(interner: &mut Interner, function: Function) -> FuncInfo {
|
||||
fn new(interner: &mut Interner, function: Function, offset: i64) -> FuncInfo {
|
||||
if PRINT_FUNCS_AND_LINES {
|
||||
eprintln!(
|
||||
"FUNC 0x{:x} size={} func={}",
|
||||
function.address,
|
||||
"FUNC {} size={} func={}",
|
||||
format_address(function.address, offset),
|
||||
function.size,
|
||||
function.name.as_str()
|
||||
);
|
||||
}
|
||||
FuncInfo {
|
||||
address: function.address,
|
||||
address: (function.address as i64 + offset) as u64,
|
||||
size: function.size,
|
||||
mangled_name: function.name.as_str().to_string(),
|
||||
line_infos: function
|
||||
.lines
|
||||
.into_iter()
|
||||
.map(|line| LineInfo::new(interner, line))
|
||||
.map(|line| LineInfo::new(interner, line, offset))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
@ -146,10 +159,12 @@ impl FuncInfo {
|
|||
}
|
||||
|
||||
/// Debug info for a single file.
|
||||
#[derive(Default)]
|
||||
struct FileInfo {
|
||||
interner: Interner,
|
||||
|
||||
/// The `FuncInfo`s are sorted by `address`.
|
||||
func_infos: Vec<FuncInfo>,
|
||||
interner: Interner,
|
||||
}
|
||||
|
||||
impl FileInfo {
|
||||
|
@ -160,18 +175,60 @@ impl FileInfo {
|
|||
.functions()
|
||||
.filter_map(|function| {
|
||||
let function = function.ok()?;
|
||||
Some(FuncInfo::new(&mut interner, function))
|
||||
Some(FuncInfo::new(&mut interner, function, 0))
|
||||
})
|
||||
.collect();
|
||||
func_infos.sort_unstable_by_key(|func_info| func_info.address);
|
||||
func_infos.dedup_by_key(|func_info| func_info.address);
|
||||
|
||||
FileInfo {
|
||||
interner,
|
||||
func_infos,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the debug info from `debug_session` for functions in `filename`
|
||||
/// to that already gathered in `interner` and `func_infos`, but only for
|
||||
/// functions present in `sym_func_addrs`.
|
||||
fn add(
|
||||
sym_func_addrs: &SymFuncAddrs,
|
||||
file_name: &str,
|
||||
debug_session: ObjectDebugSession,
|
||||
interner: &mut Interner,
|
||||
func_infos: &mut Vec<FuncInfo>,
|
||||
) {
|
||||
// Build the `FileInfo` from the debug session.
|
||||
func_infos.extend(debug_session.functions().filter_map(|function| {
|
||||
let function = function.ok()?;
|
||||
|
||||
// If a function appears in the debug info but was not seen in
|
||||
// the parent binary's symbol table, just ignore it. This is
|
||||
// common, perhaps due to inlining (i.e. inlined functions don't
|
||||
// end up with their own symbols in the binary).
|
||||
//
|
||||
// Otherwise, we know the function's address in the parent binary's
|
||||
// symbol table. Adjust all the addresses from the debug info to
|
||||
// match that address.
|
||||
let sym_func_key = Fixer::sym_func_key(file_name, function.name.as_str());
|
||||
let sym_func_addr = sym_func_addrs.get(&sym_func_key)?;
|
||||
let offset = *sym_func_addr as i64 - function.address as i64;
|
||||
Some(FuncInfo::new(interner, function, offset))
|
||||
}));
|
||||
}
|
||||
|
||||
/// Finish constructing a `FileInfo` that has been built up using
|
||||
/// `Fixer::add`.
|
||||
fn finish(interner: Interner, mut func_infos: Vec<FuncInfo>) -> FileInfo {
|
||||
func_infos.sort_unstable_by_key(|func_info| func_info.address);
|
||||
func_infos.dedup_by_key(|func_info| func_info.address);
|
||||
|
||||
FileInfo {
|
||||
func_infos,
|
||||
interner,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `FuncInfo` for an address, if there is one.
|
||||
fn func_info(&self, address: u64) -> Option<&FuncInfo> {
|
||||
match self
|
||||
.func_infos
|
||||
|
@ -198,6 +255,9 @@ struct Fixer {
|
|||
json_escaping: JsonEscaping,
|
||||
}
|
||||
|
||||
/// Records address of functions from a symbol table.
|
||||
type SymFuncAddrs = FxHashMap<String, u64>;
|
||||
|
||||
impl Fixer {
|
||||
fn new(json_escaping: JsonEscaping) -> Fixer {
|
||||
Fixer {
|
||||
|
@ -220,38 +280,255 @@ impl Fixer {
|
|||
/// subsequently query. Return a description of the failing operation on
|
||||
/// error.
|
||||
fn build_file_info(file_name: &str) -> Result<FileInfo, String> {
|
||||
let msg = |op: &str| format!("Unable to {} `{}`", op, file_name);
|
||||
let data = fs::read(file_name).map_err(|_| "read")?;
|
||||
let file_format = Archive::peek(&data);
|
||||
match file_format {
|
||||
FileFormat::Elf => Fixer::build_file_info_direct(&data),
|
||||
FileFormat::Pe => Fixer::build_file_info_pe(&data),
|
||||
FileFormat::Pdb => Fixer::build_file_info_direct(&data),
|
||||
FileFormat::MachO => Fixer::build_file_info_macho(&data),
|
||||
_ => Err(format!("parse {} format file", file_format)),
|
||||
}
|
||||
}
|
||||
|
||||
// Read the file.
|
||||
let mut data = fs::read(file_name).map_err(|_| msg("read"))?;
|
||||
// "Direct" means that the debug info is within `data`, as opposed to being
|
||||
// in another file that `data` refers to.
|
||||
fn build_file_info_direct(data: &[u8]) -> Result<FileInfo, String> {
|
||||
let object = Object::parse(&data).map_err(|_| "parse")?;
|
||||
let debug_session = object.debug_session().map_err(|_| "read debug info from")?;
|
||||
Ok(FileInfo::new(debug_session))
|
||||
}
|
||||
|
||||
// On some platforms we have to get the debug info from another file.
|
||||
// Get the name of that file, if there is one.
|
||||
let file_name2 = match Object::peek(&data) {
|
||||
FileFormat::Pe => {
|
||||
let pe_object = Object::parse(&data).map_err(|_| msg("parse"))?;
|
||||
if let Object::Pe(pe) = pe_object {
|
||||
// PE files should contain a pointer to a PDB file.
|
||||
let pdb_file_name = pe.debug_file_name().ok_or_else(|| msg("find PDB for"))?;
|
||||
Some(pdb_file_name.to_string())
|
||||
fn build_file_info_pe(data: &[u8]) -> Result<FileInfo, String> {
|
||||
// For PEs we get the debug info from a PDB file.
|
||||
let pe_object = Object::parse(&data).map_err(|_| "parse")?;
|
||||
let pe = match pe_object {
|
||||
Object::Pe(pe) => pe,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let pdb_file_name = pe.debug_file_name().ok_or("find debug info file for")?;
|
||||
let data = fs::read(pdb_file_name.to_string())
|
||||
.map_err(|_| format!("read debug info file `{}` for", pdb_file_name))?;
|
||||
Fixer::build_file_info_direct(&data)
|
||||
}
|
||||
|
||||
fn build_file_info_macho(data: &[u8]) -> Result<FileInfo, String> {
|
||||
// On Mac, debug info is typically stored in `.dSYM` directories. But
|
||||
// they aren't normally built for Firefox because doing so is slow.
|
||||
// Instead, we read the symbol table of the given file, which has
|
||||
// pointers to all the object files from which it was constructed. We
|
||||
// then obtain the debug info from those object files (some of which
|
||||
// are embedded within `.a` files), and adjust the addresses from the
|
||||
// debug info appropriately. All this requires the object files to
|
||||
// still be present, and matches what `atos` does.
|
||||
//
|
||||
// Doing all this requires a lower level of processing than what the
|
||||
// `symbolic` crate provides, so instead we use the `goblin` crate.
|
||||
//
|
||||
// We stop if any errors are encountered. The code could be made more
|
||||
// robust in the face of errors if necessary.
|
||||
|
||||
let macho = Fixer::macho(&data)?;
|
||||
let sym_func_addrs = Fixer::sym_func_addrs(&macho)?;
|
||||
|
||||
// Iterate again through the symbol table, reading every object file
|
||||
// that is referenced, and adjusting the addresses in those files using
|
||||
// the function addresses obtained above.
|
||||
let mut seen_archives = FxHashSet::default();
|
||||
let mut func_infos = vec![];
|
||||
let mut interner = Interner::default();
|
||||
for sym in macho.symbols() {
|
||||
let (oso_name, nlist) = sym.map_err(|_| "read symbol table from")?;
|
||||
if nlist.is_stab() && nlist.n_type == mach::symbols::N_OSO {
|
||||
if let Some(ar_file_name) = Fixer::is_within_archive(oso_name) {
|
||||
// It's an archive entry, e.g. "libgkrust.a(foo.o)". Read
|
||||
// every entry in archive, if we haven't already done so.
|
||||
if seen_archives.insert(ar_file_name) {
|
||||
let ar_data = fs::read(ar_file_name)
|
||||
.map_err(|_| format!("read ar `{}` referenced by", ar_file_name))?;
|
||||
let ar = archive::Archive::parse(&ar_data)
|
||||
.map_err(|_| format!("parse ar `{}` referenced by", ar_file_name))?;
|
||||
|
||||
for (name, _, _) in ar.summarize() {
|
||||
let data = ar.extract(name, &ar_data).map_err(|_| {
|
||||
format!("read an entry in ar `{}` referenced by", ar_file_name)
|
||||
})?;
|
||||
Fixer::do_macho_oso(
|
||||
&sym_func_addrs,
|
||||
ar_file_name,
|
||||
data,
|
||||
&mut interner,
|
||||
&mut func_infos,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!(); // Impossible: peek() said it was a PE object.
|
||||
// It's a normal object file. Read it.
|
||||
let data = fs::read(oso_name)
|
||||
.map_err(|_| format!("read `{}` referenced by", oso_name))?;
|
||||
Fixer::do_macho_oso(
|
||||
&sym_func_addrs,
|
||||
oso_name,
|
||||
&data,
|
||||
&mut interner,
|
||||
&mut func_infos,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(file_name2) = file_name2 {
|
||||
data = fs::read(&file_name2)
|
||||
.map_err(|_| msg(&format!("read debug info file `{}` for", file_name2)))?;
|
||||
}
|
||||
|
||||
// Get the debug session from the file data.
|
||||
let object = Object::parse(&data).map_err(|_| msg("parse"))?;
|
||||
Ok(FileInfo::finish(interner, func_infos))
|
||||
}
|
||||
|
||||
fn macho(data: &[u8]) -> Result<mach::MachO, String> {
|
||||
let mach = mach::Mach::parse(&data).map_err(|_| "parse (with goblin)")?;
|
||||
match mach {
|
||||
mach::Mach::Binary(macho) => Ok(macho),
|
||||
mach::Mach::Fat(multi_arch) => {
|
||||
// Get the x86-64 object from the fat binary. (On Mac, Firefox
|
||||
// is only available on x86-64.)
|
||||
let macho = multi_arch.into_iter().find(|macho| {
|
||||
if let Ok(macho) = macho {
|
||||
if macho.header.cputype() == mach::constants::cputype::CPU_TYPE_X86_64 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
|
||||
// This chaining is necessary because `MachOIterator::Item` is
|
||||
// not `MachO` but `Result<MachO>`. We don't distinguish
|
||||
// between the "couldn't find the x86-64 code" case and the
|
||||
// "found it, but it had an error" case.
|
||||
let msg = "find x86-64 code in the fat binary";
|
||||
macho.ok_or(msg)?.map_err(|_| msg.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate through the symbol table, getting the address of every function
|
||||
/// in the file.
|
||||
fn sym_func_addrs(macho: &mach::MachO) -> Result<SymFuncAddrs, String> {
|
||||
let object_load_address = Fixer::object_load_address(macho);
|
||||
let mut sym_func_addrs = FxHashMap::default();
|
||||
let mut curr_oso_name = String::new();
|
||||
for sym in macho.symbols() {
|
||||
let (name, nlist) = sym.map_err(|_| "read symbol table from")?;
|
||||
if !nlist.is_stab() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if nlist.n_type == mach::symbols::N_OSO {
|
||||
// Record this reference to an object file (or archive).
|
||||
curr_oso_name = if let Some(ar_file_name) = Fixer::is_within_archive(name) {
|
||||
// We have to strip the archive suffix, because the suffix
|
||||
// in the symbol table often disagrees with the suffix in
|
||||
// the debug info. E.g.
|
||||
// - symbol table: `libjs_static.a(Unified_cpp_js_src9.o)`
|
||||
// - debug info: `libjs_static.a(RegExp.o)`
|
||||
//
|
||||
// It's unclear why this occurs, though it doesn't seem to
|
||||
// matter much, though see the comment about duplicates
|
||||
// below.
|
||||
ar_file_name.to_string()
|
||||
} else {
|
||||
name.to_string()
|
||||
}
|
||||
} else if nlist.n_type == mach::symbols::N_FUN
|
||||
&& nlist.n_sect != mach::symbols::NO_SECT as usize
|
||||
{
|
||||
let name = &name[1..]; // Trim the leading underscore.
|
||||
let address = nlist.n_value - object_load_address;
|
||||
|
||||
let sym_func_key = Fixer::sym_func_key(&curr_oso_name, name);
|
||||
|
||||
// There can be duplicates, in which case the last one "wins".
|
||||
// This seems to only occur due to the removal of archive
|
||||
// suffixes above. In practice it doesn't seem to matter.
|
||||
sym_func_addrs.insert(sym_func_key, address);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(sym_func_addrs)
|
||||
}
|
||||
|
||||
// This is based on `symbolic_debuginfo::macho:MachObject::load_address`.
|
||||
// We need to define it because `goblin` doesn't have an equivalent, and we
|
||||
// use `goblin` rather than `symbolic_debuginfo` for Mach-O binaries.
|
||||
fn object_load_address(macho: &mach::MachO) -> u64 {
|
||||
for seg in macho.segments.iter() {
|
||||
if let Ok(name) = seg.name() {
|
||||
if name == "__TEXT" {
|
||||
return seg.vmaddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Construct a key for the `sym_func_addrs` hash map.
|
||||
fn sym_func_key(file_name: &str, func_name: &str) -> String {
|
||||
format!("{}:{}", file_name, func_name)
|
||||
}
|
||||
|
||||
/// Is this filename within an archive? E.g. `libfoo.a(bar.o)` means that
|
||||
/// `bar.o` is within the archive `libfoo.a`. If so, return the archive
|
||||
/// name.
|
||||
fn is_within_archive(file_name: &str) -> Option<&str> {
|
||||
if let (Some(index), true) = (file_name.find(".a("), file_name.ends_with(")")) {
|
||||
let ar_file_name = &file_name[..index + 2];
|
||||
Some(ar_file_name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the debug info from a file referenced by an OSO entry in a Macho-O
|
||||
/// symbol table.
|
||||
fn do_macho_oso(
|
||||
sym_func_addrs: &SymFuncAddrs,
|
||||
file_name: &str,
|
||||
data: &[u8],
|
||||
interner: &mut Interner,
|
||||
func_infos: &mut Vec<FuncInfo>,
|
||||
) -> Result<(), String> {
|
||||
// Although we use `goblin` to iterate through the symbol
|
||||
// table, we use `symbolic` to read the debug info from the
|
||||
// object/archive, because it's easier to use.
|
||||
let archive = Archive::parse(&data)
|
||||
.map_err(|e| format!("({:?}) parse `{}` referenced by", e, file_name))?;
|
||||
|
||||
// Get the x86-64 object from the archive, which might be a fat binary.
|
||||
// (On Mac, Firefox is only available on x86-64.)
|
||||
let mut x86_64_object = None;
|
||||
for object in archive.objects() {
|
||||
let object = object
|
||||
.map_err(|_| format!("parse fat binary entry in `{}` referenced by", file_name))?;
|
||||
if object.arch() == Arch::Amd64 {
|
||||
x86_64_object = Some(object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let object = x86_64_object.ok_or_else(|| {
|
||||
format!(
|
||||
"find x86-64 code in the fat binary `{}` referenced by",
|
||||
file_name
|
||||
)
|
||||
})?;
|
||||
let debug_session = object
|
||||
.debug_session()
|
||||
.map_err(|_| msg("read debug info from"))?;
|
||||
.map_err(|_| format!("read debug info from `{}` referenced by", file_name))?;
|
||||
|
||||
Ok(FileInfo::new(debug_session))
|
||||
FileInfo::add(
|
||||
&sym_func_addrs,
|
||||
file_name,
|
||||
debug_session,
|
||||
interner,
|
||||
func_infos,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fix stack frames within `line` as necessary. Prints any errors to stderr.
|
||||
|
@ -276,9 +553,16 @@ impl Fixer {
|
|||
Entry::Occupied(o) => o.into_mut(),
|
||||
Entry::Vacant(v) => match Fixer::build_file_info(file_name) {
|
||||
Ok(file_info) => v.insert(file_info),
|
||||
Err(msg) => {
|
||||
eprintln!("{}", msg);
|
||||
return line;
|
||||
Err(op) => {
|
||||
// Print an error message and then set up an empty
|
||||
// `FileInfo` for this file, for two reasons.
|
||||
// - If an invalid file is mentioned multiple times in the
|
||||
// input, an error message will be issued only on the
|
||||
// first occurrence.
|
||||
// - The line will still receive some transformation, using
|
||||
// the "no symbols or debug info" case below.
|
||||
eprintln!("fix-stacks error: failed to {} `{}`", op, file_name);
|
||||
v.insert(FileInfo::default())
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
113
src/tests.rs
113
src/tests.rs
|
@ -33,7 +33,7 @@ fn test_linux() {
|
|||
|
||||
let mut fixer = Fixer::new(JsonEscaping::No);
|
||||
|
||||
// Test various addresses within `main`.
|
||||
// Test various addresses.
|
||||
let mut func = |name, addr, linenum| {
|
||||
let line = format!("#00: ???[tests/example-linux +0x{:x}]", addr);
|
||||
let line = fixer.fix(line);
|
||||
|
@ -111,8 +111,8 @@ fn test_windows() {
|
|||
|
||||
let mut fixer = Fixer::new(JsonEscaping::Yes);
|
||||
|
||||
// Test various addresses within `main` using `example-windows`, which
|
||||
// redirects to `example-windows.pdb`.
|
||||
// Test various addresses using `example-windows`, which redirects to
|
||||
// `example-windows.pdb`.
|
||||
let mut func = |name, addr, linenum| {
|
||||
let line = format!("#00: ???[tests/example-windows +0x{:x}]", addr);
|
||||
let line = fixer.fix(line);
|
||||
|
@ -158,6 +158,109 @@ fn test_windows() {
|
|||
outside(0xfffffff); // A very high address.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mac() {
|
||||
// The debug info within `mac-multi` is as follows. (See `tests/README.md`
|
||||
// for details on how these lines were generated.)
|
||||
//
|
||||
// FUNC 0xd70 size=54 func=main
|
||||
// LINE 0xd70 line=17 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
// LINE 0xd7f line=18 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
// LINE 0xd86 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
// LINE 0xd8f line=20 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
// LINE 0xd98 line=21 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
// LINE 0xd9d line=22 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
//
|
||||
// FUNC 0xdb0 size=31 func=duplicate
|
||||
// LINE 0xdb0 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
// LINE 0xdb8 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
// LINE 0xdc9 line=12 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
|
||||
//
|
||||
// FUNC 0xdd0 size=37 func=fat_B
|
||||
// LINE 0xdd0 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
// LINE 0xddc line=20 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
// LINE 0xdef line=21 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
//
|
||||
// FUNC 0xe00 size=34 func=fat_A
|
||||
// LINE 0xe00 line=13 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
// LINE 0xe0b line=14 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
// LINE 0xe14 line=15 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
// LINE 0xe19 line=16 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
//
|
||||
// FUNC 0xe30 size=31 func=duplicate
|
||||
// LINE 0xe30 line=9 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
// LINE 0xe38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
// LINE 0xe49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
|
||||
//
|
||||
// FUNC 0xe50 size=37 func=lib1_B
|
||||
// LINE 0xe50 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
// LINE 0xe5c line=20 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
// LINE 0xe6f line=21 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
//
|
||||
// FUNC 0xe80 size=34 func=lib1_A
|
||||
// LINE 0xe80 line=13 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
// LINE 0xe8b line=14 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
// LINE 0xe94 line=15 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
// LINE 0xe99 line=16 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
//
|
||||
// // Note that these ones are wrong, and should start at 0xeb0. This is
|
||||
// // due to the unavoidable archive suffix stripping, see comments in
|
||||
// // `main.rs` for details.
|
||||
// FUNC 0xf30 size=31 func=duplicate
|
||||
// LINE 0xf30 line=9 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
// LINE 0xf38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
// LINE 0xf49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
|
||||
//
|
||||
// FUNC 0xed0 size=37 func=lib2_B
|
||||
// LINE 0xed0 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
// LINE 0xedc line=20 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
// LINE 0xeef line=21 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
//
|
||||
// FUNC 0xf00 size=39 func=lib2_A
|
||||
// LINE 0xf00 line=13 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
// LINE 0xf0b line=14 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
// LINE 0xf19 line=15 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
// LINE 0xf1e line=16 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
//
|
||||
// FUNC 0xf30 size=31 func=duplicate
|
||||
// LINE 0xf30 line=9 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
// LINE 0xf38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
// LINE 0xf49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||
|
||||
let mut fixer = Fixer::new(JsonEscaping::No);
|
||||
|
||||
// Test addresses from all the object files that `mac-multi` references.
|
||||
let mut func = |name, addr, full_path, locn| {
|
||||
let line = format!("#00: ???[tests/mac-multi +0x{:x}]", addr);
|
||||
let line = fixer.fix(line);
|
||||
let path = if full_path {
|
||||
"/Users/njn/moz/fix-stacks/tests/"
|
||||
} else {
|
||||
"tests/"
|
||||
};
|
||||
assert_eq!(line, format!("#00: {} ({}{})", name, path, locn));
|
||||
};
|
||||
|
||||
func("main", 0xd70, true, "mac-normal.c:17");
|
||||
func("duplicate", 0xdb3, true, "mac-normal.c:10");
|
||||
|
||||
func("fat_B", 0xddc, true, "mac-fat.c:20");
|
||||
func("fat_A", 0xe19, true, "mac-fat.c:16");
|
||||
func("duplicate", 0xe4e, true, "mac-fat.c:11");
|
||||
|
||||
func("lib1_B", 0xe50, true, "mac-lib1.c:19");
|
||||
func("lib1_A", 0xe95, true, "mac-lib1.c:15");
|
||||
// This should be `duplicate` in `mac-lib1.c`. It's wrong due to the
|
||||
// archive suffix stripping mentioned above.
|
||||
func("???", 0xeaa, false, "mac-multi");
|
||||
|
||||
func("lib2_B", 0xedc, true, "mac-lib2.c:20");
|
||||
func("lib2_A", 0xf1e, true, "mac-lib2.c:16");
|
||||
// This should be `mac-lib2.c:10`. It's wrong due to the archive suffix
|
||||
// stripping mentioned above.
|
||||
func("duplicate", 0xf38, true, "mac-lib1.c:10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex() {
|
||||
let mut fixer = Fixer::new(JsonEscaping::No);
|
||||
|
@ -176,8 +279,8 @@ fn test_regex() {
|
|||
|
||||
// An error message is also printed to stderr for file errors, but we don't
|
||||
// test for that.
|
||||
unchanged("#00: ???[tests/no-such-file +0x43a0]"); // No such file.
|
||||
unchanged("#00: ???[src/main.rs +0x43a0]"); // File exists, but has wrong format.
|
||||
unchanged("#00: ??? (tests/no-such-file)"); // No such file.
|
||||
unchanged("#00: ??? (src/main.rs)"); // File exists, but has wrong format.
|
||||
|
||||
// Test various different changed line forms that do match the regex.
|
||||
let mut changed = |line1: &str, line2_expected| {
|
||||
|
|
|
@ -22,19 +22,56 @@ To allow this requires the following.
|
|||
## Generating inputs
|
||||
|
||||
Debug info is very complicated and hard to write by hand. Therefore, the tests
|
||||
use executables and other data files created by compilers as inputs.
|
||||
use executables and other data files created by compilers as inputs. These were
|
||||
all generated within the `test` directory in the following ways.
|
||||
|
||||
The primary source code file used for generating these files is
|
||||
`tests/example.c`. The following files were generated from it.
|
||||
- `example-linux`: produced on an Ubuntu 19.04 box by clang 8.0 with the
|
||||
command `clang -g example.c -o example-linux`.
|
||||
- `example-windows` and `example-windows.pdb`: produced on a Windows 10 laptop
|
||||
by clang 9.0 with the command `clang -g example.c -o example-windows`.
|
||||
`example-windows` was then hex-edited to change the PDB reference from the
|
||||
absolute path `c:\Users\njn\moz\fix-stacks\tests\example-windows.pdb` to the
|
||||
relative path `tests/////////////////////////////example-windows.pdb`. (The
|
||||
use of many redundant forward slashes is a hack to keep the path the same
|
||||
length, which avoids the need for more complex changes to that file.)
|
||||
### Linux
|
||||
|
||||
`example-linux` was produced on an Ubuntu 19.04 box by clang 8.0 with this
|
||||
command:
|
||||
```
|
||||
clang -g example.c -o example-linux
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
`example-windows` and `example-windows.pdb` were produced on a Windows 10 laptop
|
||||
by clang 9.0 with this command:
|
||||
```
|
||||
clang -g example.c -o example-windows
|
||||
```
|
||||
`example-windows` was then hex-edited to change the PDB reference from the
|
||||
absolute path `c:\Users\njn\moz\fix-stacks\tests\example-windows.pdb` to the
|
||||
relative path `tests/////////////////////////////example-windows.pdb`. (The use
|
||||
of many redundant forward slashes is a hack to keep the path the same length,
|
||||
which avoids the need for more complex changes to that file.)
|
||||
|
||||
### Mac
|
||||
|
||||
The Mac tests are more complex because `fix-stacks`'s code for handling Mach-O
|
||||
binaries is more complex than other formats.
|
||||
|
||||
`mac-multi` was produced on a MacBook Pro running macOS 10.14 by Apple clang
|
||||
11.0 with these commands:
|
||||
```
|
||||
# A normal file.
|
||||
clang -c -g mac-normal.c -o mac-normal.o
|
||||
# A fat binary.
|
||||
clang -m32 -c -g mac-fat.c -o mac-fat-32.o
|
||||
clang -m64 -c -g mac-fat.c -o mac-fat-64.o
|
||||
lipo -create mac-fat-32.o mac-fat-64.o -output mac-fat.o
|
||||
# A library.
|
||||
clang -c -g mac-lib1.c -o mac-lib1.o
|
||||
clang -c -g mac-lib2.c -o mac-lib2.o
|
||||
ar -r libexample.a mac-lib1.o mac-lib2.o
|
||||
# The final executable.
|
||||
clang mac-normal.o mac-fat.o libexample.a -o mac-multi
|
||||
```
|
||||
`mac-multi` was then hex-edited to change all the file reference from the
|
||||
absolute paths such as `/Users/njn/moz/fix-stacks/tests/mac-normal.c` to the
|
||||
relative paths such as `tests///////////////////////////mac-normal.c`. (The use
|
||||
of many redundant forward slashes is a hack to keep the path the same length,
|
||||
which avoids the need for more complex changes to that file.)
|
||||
|
||||
## Obtaining the debug info
|
||||
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,21 @@
|
|||
// This file contains the source code for some tests. If you change this file
|
||||
// you may need to regenerate some test files. (Even changing the number of
|
||||
// lines in this comment would have an effect.)
|
||||
//
|
||||
// See `tests/README.md` for more details.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void duplicate() {
|
||||
printf("fat duplicate");
|
||||
}
|
||||
|
||||
static int fat_A(int x) {
|
||||
x = x * 2;
|
||||
duplicate();
|
||||
return x;
|
||||
}
|
||||
|
||||
void fat_B(int* x) {
|
||||
*x += fat_A(*x);
|
||||
}
|
Двоичный файл не отображается.
|
@ -0,0 +1,21 @@
|
|||
// This file contains the source code for some tests. If you change this file
|
||||
// you may need to regenerate some test files. (Even changing the number of
|
||||
// lines in this comment would have an effect.)
|
||||
//
|
||||
// See `tests/README.md` for more details.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void duplicate() {
|
||||
printf("lib1 duplicate");
|
||||
}
|
||||
|
||||
static int lib1_A(int x) {
|
||||
x = x + 2;
|
||||
duplicate();
|
||||
return x;
|
||||
}
|
||||
|
||||
void lib1_B(int* x) {
|
||||
*x += lib1_A(*x);
|
||||
}
|
Двоичный файл не отображается.
|
@ -0,0 +1,21 @@
|
|||
// This file contains the source code for some tests. If you change this file
|
||||
// you may need to regenerate some test files. (Even changing the number of
|
||||
// lines in this comment would have an effect.)
|
||||
//
|
||||
// See `tests/README.md` for more details.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void duplicate() {
|
||||
printf("lib2 duplicate");
|
||||
}
|
||||
|
||||
static int lib2_A(int x) {
|
||||
x = x / 2;
|
||||
duplicate();
|
||||
return x;
|
||||
}
|
||||
|
||||
void lib2_B(int* x) {
|
||||
*x += lib2_A(*x);
|
||||
}
|
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,23 @@
|
|||
// This file contains the source code for some tests. If you change this file
|
||||
// you may need to regenerate some test files. (Even changing the number of
|
||||
// lines in this comment would have an effect.)
|
||||
//
|
||||
// See `tests/README.md` for more details.
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void duplicate() {
|
||||
printf("normal duplicate");
|
||||
}
|
||||
|
||||
void lib1_B(int* x);
|
||||
void lib2_B(int* x);
|
||||
|
||||
int main() {
|
||||
int x = 0;
|
||||
lib1_B(&x);
|
||||
lib2_B(&x);
|
||||
duplicate();
|
||||
return x;
|
||||
}
|
Двоичный файл не отображается.
Загрузка…
Ссылка в новой задаче