Compare commits
1143 Commits
linux-rewr
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
7fc25b8962 | ||
|
c65ed6573e | ||
|
a34c929f6f | ||
|
72e94c4ddb | ||
|
7933830cb7 | ||
|
240ed57ee1 | ||
|
8b7b28663f | ||
|
d269bd0f29 | ||
|
bad9e55ea5 | ||
|
6368037ef2 | ||
|
983d04d36e | ||
|
71a5ee37a5 | ||
|
79901bb99d | ||
|
f25a411200 | ||
|
faeb1f9b2b | ||
|
adca10c8ff | ||
|
e35167de75 | ||
|
9eb2c6fe6c | ||
|
67d5567177 | ||
|
a875991e73 | ||
|
a84d4fc426 | ||
|
3d225e288c | ||
|
1bbe2f29c9 | ||
|
638066b82e | ||
|
764c21f752 | ||
|
b5712766f6 | ||
|
4f417c8c0f | ||
|
fcb844ee32 | ||
|
17ad0c18b1 | ||
|
8762c82297 | ||
|
ea2e22b352 | ||
|
d48fa9d3a4 | ||
|
622bc036d6 | ||
|
b39ab626ab | ||
|
d986b9d311 | ||
|
2e266396ee | ||
|
fee47f4638 | ||
|
0203b0cbdd | ||
|
205b5ac6ed | ||
|
2f1b7adcd3 | ||
|
b0e5f26233 | ||
|
32901f6d4c | ||
|
46359276c8 | ||
|
6c8f80aa8b | ||
|
c1848484e1 | ||
|
6ce752d741 | ||
|
d9c532217e | ||
|
c77c51fae6 | ||
|
d091e5103f | ||
|
512cac3a1b | ||
|
85b27e4a69 | ||
|
ebba1800e4 | ||
|
139693446b | ||
|
11e17136db | ||
|
8915f7be39 | ||
|
4d69050717 | ||
|
f3d28e9c4a | ||
|
f97e47e55e | ||
|
25f913e981 | ||
|
7b25f70813 | ||
|
358c3b449c | ||
|
dee15c2547 | ||
|
e132514d80 | ||
|
d7c0bd0624 | ||
|
df280bb631 | ||
|
a8f79b0fde | ||
|
4912669df1 | ||
|
afa2539de4 | ||
|
c4c887c34a | ||
|
f4012f5cd9 | ||
|
76c51742c9 | ||
|
c199f5b122 | ||
|
815d51c343 | ||
|
442b45266d | ||
|
3f736ce086 | ||
|
bc44efdc27 | ||
|
b16c640e09 | ||
|
f820ead901 | ||
|
4b5dcd0e69 | ||
|
83523e066b | ||
|
e4cf26b851 | ||
|
92280b3c67 | ||
|
aa5ceea25b | ||
|
658eb44f3f | ||
|
8baa236daa | ||
|
48673cdb8c | ||
|
126a938fef | ||
|
081cbd64d7 | ||
|
ac69c577d8 | ||
|
4f512397f5 | ||
|
a1784ae81f | ||
|
2dda5fefdc | ||
|
68039850bc | ||
|
7b0d7a976e | ||
|
728bcdff61 | ||
|
98bea93a3e | ||
|
bcf4cc2f9a | ||
|
b1d3cdba8b | ||
|
8c49d62005 | ||
|
ee4d8ffed4 | ||
|
f9b60452e1 | ||
|
257123ca70 | ||
|
1be3027941 | ||
|
524508ff2f | ||
|
a22992081f | ||
|
dc16e41747 | ||
|
f217ae431f | ||
|
39e372dab6 | ||
|
fc62e72539 | ||
|
2d1b97a1ff | ||
|
ac2a3e466f | ||
|
ee69060bfd | ||
|
beee5c2e71 | ||
|
aa5ff205cb | ||
|
b458f36762 | ||
|
ef7575d35a | ||
|
a31f601d90 | ||
|
4ee6f3cd1c | ||
|
7685dfc567 | ||
|
dbf56c53a0 | ||
|
f948d8d623 | ||
|
01c8f7cf46 | ||
|
679d70f50b | ||
|
0d53bdd43d | ||
|
08a727d8f0 | ||
|
f74c85df88 | ||
|
0ff15b41ed | ||
|
4d06db5a1f | ||
|
3520fc314b | ||
|
8079ae192d | ||
|
d28bebcd16 | ||
|
1dcec01445 | ||
|
a057bc5d3e | ||
|
e8dbddf291 | ||
|
2d450f312b | ||
|
26ce7a2a89 | ||
|
3f4d090d70 | ||
|
f073540795 | ||
|
ee2d76fe90 | ||
|
8e8be001cb | ||
|
9178d8fa03 | ||
|
3bc3dad8f8 | ||
|
3365fe7986 | ||
|
dc5c38675c | ||
|
6ea9219d80 | ||
|
a3e9c02647 | ||
|
e3687f92c2 | ||
|
ab5ffb8b6c | ||
|
8794e39888 | ||
|
cde8616abf | ||
|
591ba326b3 | ||
|
ffa293221f | ||
|
c4e643dfb4 | ||
|
6a1a906773 | ||
|
47527e48e5 | ||
|
64c0d9c66d | ||
|
0dbae0cf3f | ||
|
4634d88f2e | ||
|
2997f45907 | ||
|
4959c18e98 | ||
|
0b7c158932 | ||
|
85e4cf8029 | ||
|
a840e1b710 | ||
|
0c426387b8 | ||
|
28c9c9b82c | ||
|
fed2e33bd1 | ||
|
e2eb4505d0 | ||
|
12213a5e15 | ||
|
440c1f79ef | ||
|
cd744acecc | ||
|
cca153368a | ||
|
ed3bf1c825 | ||
|
3c3c6680d3 | ||
|
11004944ad | ||
|
893e93a41f | ||
|
8785c0cb8f | ||
|
66295d1352 | ||
|
90cbb27528 | ||
|
f1ca06a079 | ||
|
69b745aa74 | ||
|
aa75fa5d4e | ||
|
0f27af628f | ||
|
9c6af3e29c | ||
|
50b2a7e647 | ||
|
751453c357 | ||
|
d88401cca9 | ||
|
18fc88ddc2 | ||
|
353e4cd585 | ||
|
cd6f90524a | ||
|
cfdf7cf8a9 | ||
|
48b9a40539 | ||
|
9b2d7192a4 | ||
|
ee25f6794b | ||
|
9208fbc224 | ||
|
bbd478ae73 | ||
|
eb08c16300 | ||
|
f2e6c177e4 | ||
|
14bbefbd96 | ||
|
b79454627e | ||
|
94f863af2c | ||
|
3b7262b0ee | ||
|
c63cf36a13 | ||
|
89947e7d86 | ||
|
854b9f74a0 | ||
|
9f00bce09a | ||
|
4c01a9c389 | ||
|
8aa26b79f9 | ||
|
924a8a2974 | ||
|
cef1bee150 | ||
|
cfaf27c37e | ||
|
7bebb27fb4 | ||
|
efa0310455 | ||
|
c332a1dcba | ||
|
30d41ec243 | ||
|
ba16fd3ffc | ||
|
d10c8c18ae | ||
|
bbbe29de11 | ||
|
10baadae40 | ||
|
0b6cc95246 | ||
|
63919583ba | ||
|
57e5b074f6 | ||
|
be21ad8190 | ||
|
6354cffeb0 | ||
|
e469f8cf4b | ||
|
6690554c4b | ||
|
8127f56dff | ||
|
c971cf439c | ||
|
507a09f4de | ||
|
e20758c298 | ||
|
c1c257f4f6 | ||
|
e1be1aa1ec | ||
|
a1d7ad0c79 | ||
|
8453125ca5 | ||
|
719546275a | ||
|
962f1b0e60 | ||
|
df09b1ac29 | ||
|
a4834470c7 | ||
|
9f150ee33e | ||
|
75690d56b2 | ||
|
df3257f5cb | ||
|
37a3b8d997 | ||
|
b5c662fcfe | ||
|
7a6d769eb1 | ||
|
82d0ff76a6 | ||
|
63a2ea9245 | ||
|
18b1418b25 | ||
|
85cc448052 | ||
|
61d4225325 | ||
|
def0b7a7b0 | ||
|
c75e316359 | ||
|
7883a83082 | ||
|
1c822b7768 | ||
|
cf22feeecd | ||
|
54c2560a82 | ||
|
a58de86bcd | ||
|
1335417f9f | ||
|
8cde5c5152 | ||
|
eee1801a91 | ||
|
d7ce15b47b | ||
|
1b309ed007 | ||
|
12d72ba492 | ||
|
2a5c22a580 | ||
|
c1ef53351c | ||
|
9c932f1d0c | ||
|
e2fffff1a4 | ||
|
dab50e6f6e | ||
|
c41b7fc414 | ||
|
561f818735 | ||
|
b7aa202b54 | ||
|
8703844b3c | ||
|
6c57bc19a5 | ||
|
d91bd8d23a | ||
|
d7d532bf56 | ||
|
0a5ebafdf0 | ||
|
9e8ffc9fab | ||
|
21384ad0e2 | ||
|
f88336d4a1 | ||
|
501673c86b | ||
|
bf0e00374f | ||
|
ada265ebbd | ||
|
9d62ef9311 | ||
|
a5da1e193c | ||
|
fe34e3f9fe | ||
|
20583f0560 | ||
|
91f746ec81 | ||
|
dfdc7d2966 | ||
|
a57c70484d | ||
|
3cef0e5645 | ||
|
5421a942f0 | ||
|
222ef005ed | ||
|
412b68396c | ||
|
9eb13afd54 | ||
|
31845f2bce | ||
|
f05e1037b5 | ||
|
40cd690901 | ||
|
a789d1d26b | ||
|
44ba0b2050 | ||
|
c96f4d7e01 | ||
|
c2f9501755 | ||
|
4463365304 | ||
|
3d023a3472 | ||
|
9aaae65466 | ||
|
1c8e155b3a | ||
|
d4f5ca2275 | ||
|
c83a81450c | ||
|
7cc286a5a7 | ||
|
be38be57e1 | ||
|
32031a9136 | ||
|
eebdc96430 | ||
|
6594ac922c | ||
|
a2245d6744 | ||
|
6b0703a093 | ||
|
7dbca422d4 | ||
|
347f7a10dd | ||
|
543cf832e6 | ||
|
fff818514d | ||
|
611f8b7f32 | ||
|
66460ce1d9 | ||
|
410b7024e3 | ||
|
83b9a28cf9 | ||
|
46aba5a43a | ||
|
0ac94ba617 | ||
|
33a84041e7 | ||
|
983af7cce4 | ||
|
172d57d4b4 | ||
|
ad37f5aabd | ||
|
482df94a07 | ||
|
0decfdb042 | ||
|
3e12c58449 | ||
|
acd538baf8 | ||
|
213a0698cb | ||
|
b764582a53 | ||
|
cc3542dc44 | ||
|
c5ae79b3f2 | ||
|
1df13fed15 | ||
|
70acad5df8 | ||
|
3e76b4a74b | ||
|
334b2c70f0 | ||
|
47585f7dee | ||
|
9315301a56 | ||
|
0c18b07255 | ||
|
5fcdc4f567 | ||
|
299683dba6 | ||
|
9dd2ab720e | ||
|
c590a7f322 | ||
|
0864f0afaa | ||
|
8b65346242 | ||
|
d9f582edce | ||
|
2b0db12678 | ||
|
12528d19bd | ||
|
3ea04ec479 | ||
|
1e99113fbe | ||
|
6f045df4f0 | ||
|
4a0f9bf030 | ||
|
74f640a92e | ||
|
15873e0016 | ||
|
6199766d99 | ||
|
53a689933f | ||
|
a4d055f1c2 | ||
|
d6d6230272 | ||
|
3c613e7bdb | ||
|
ba1f54c093 | ||
|
4bcf51dc79 | ||
|
b5e5d8bd30 | ||
|
88b8ac6182 | ||
|
4c048c9764 | ||
|
7f05071e8b | ||
|
cd272586ee | ||
|
b76bb512a0 | ||
|
07bb2311f8 | ||
|
302124aff1 | ||
|
8d22ec9227 | ||
|
0348c801bd | ||
|
cca536ba9d | ||
|
186f1cb206 | ||
|
616400ed2f | ||
|
31a477e889 | ||
|
d9d0a61f97 | ||
|
08ce8e35eb | ||
|
b91429acb0 | ||
|
8a0b196e22 | ||
|
036dec3ecc | ||
|
b303174e32 | ||
|
f546e072ce | ||
|
e9165f4353 | ||
|
77022ac0df | ||
|
8e28cb3898 | ||
|
46821f78b1 | ||
|
fd9f3d8b17 | ||
|
c7a1674f16 | ||
|
4b6e0bb952 | ||
|
bf139abc7d | ||
|
32cbf9fc88 | ||
|
667991e25f | ||
|
502f23211b | ||
|
6689f24d1a | ||
|
8586f6e288 | ||
|
6f3426a156 | ||
|
6fa386bfc6 | ||
|
404c684751 | ||
|
c00999fa2c | ||
|
0453082dbe | ||
|
b7acb2a56a | ||
|
bd71bdd742 | ||
|
5bce3b4d77 | ||
|
db877a13bb | ||
|
ab52ca7d6e | ||
|
8c037de0b2 | ||
|
f78839c157 | ||
|
ae35566b8d | ||
|
24c6c0aaef | ||
|
b097e31d92 | ||
|
16a880e135 | ||
|
1e57990cee | ||
|
0510e34cbf | ||
|
aaeb7e4f13 | ||
|
1d1b36d371 | ||
|
9c1dc68a2f | ||
|
437706b60a | ||
|
fce16c1856 | ||
|
8819ab0844 | ||
|
92537aa818 | ||
|
7b5811eaac | ||
|
8965a51b2c | ||
|
bbd0e39801 | ||
|
f33dd18fd2 | ||
|
d394434a93 | ||
|
374e8dcfa7 | ||
|
56cf4aa3d9 | ||
|
8041deee0c | ||
|
e10e9ac7ca | ||
|
6b46d4e872 | ||
|
5bdee65f7a | ||
|
2b1f82e1c8 | ||
|
e4d3cca3ef | ||
|
ecd727f386 | ||
|
bcf029f333 | ||
|
54e75078d6 | ||
|
3a59d0586b | ||
|
8695cc0b88 | ||
|
e775f8bb16 | ||
|
0daf8175df | ||
|
09b6b0f8bf | ||
|
3989dd415b | ||
|
09f8989389 | ||
|
7304a2c187 | ||
|
df6f45b04b | ||
|
b45b97b2fd | ||
|
85c352695f | ||
|
b43dacb23a | ||
|
3444ed7cf1 | ||
|
e61b76c679 | ||
|
110c99fbd5 | ||
|
8507f63d38 | ||
|
5b36fbe088 | ||
|
0bdc714c20 | ||
|
affac9413b | ||
|
f7566bd447 | ||
|
ae3947e123 | ||
|
77acc6adab | ||
|
593eff42ca | ||
|
9f45f6b5cf | ||
|
fb22e80229 | ||
|
40c9de4d8d | ||
|
697da828dc | ||
|
093b8aee9b | ||
|
30bb1ab601 | ||
|
9d82a70384 | ||
|
32ce6dea51 | ||
|
d9c3ad7b88 | ||
|
91c9518af2 | ||
|
1974aaba51 | ||
|
bc54c38679 | ||
|
0c759c6f62 | ||
|
d3c4cbd30f | ||
|
cff813962a | ||
|
2652675ec2 | ||
|
b6e66b294f | ||
|
6dc56cca44 | ||
|
3a8617eb8c | ||
|
b41ad66e93 | ||
|
c5ceea7d74 | ||
|
bbaca6bc00 | ||
|
704c13e7ab | ||
|
8de2cd2d22 | ||
|
637c392d1c | ||
|
ece9df9386 | ||
|
cd0eb0f8dc | ||
|
0192fc21b9 | ||
|
4867c9e9d0 | ||
|
e88f61c7ba | ||
|
8fdf481b7f | ||
|
dc64e106e2 | ||
|
7546209ddd | ||
|
60b5f7c7e9 | ||
|
718ae3284e | ||
|
983903ee0a | ||
|
46cbbb847e | ||
|
93a48343ee | ||
|
aeffe1056f | ||
|
c02002a581 | ||
|
cd04fe2c88 | ||
|
7d1e1e273a | ||
|
111cce8a88 | ||
|
7a469b1031 | ||
|
24c9dfed8a | ||
|
0513eeca07 | ||
|
bd6d0964e7 | ||
|
00407e09c7 | ||
|
52925cb9b3 | ||
|
015e79275e | ||
|
1f87c4a732 | ||
|
be81ededf8 | ||
|
d2153d7589 | ||
|
1c4f4a776c | ||
|
c6f6d9ef30 | ||
|
834f7ee611 | ||
|
7e72c6677a | ||
|
e086f8a9c8 | ||
|
a151d78133 | ||
|
63c5d98003 | ||
|
44356b590b | ||
|
a8426a5c66 | ||
|
3d451f31d8 | ||
|
bdd7220794 | ||
|
6aab1f92bb | ||
|
b7aeb98608 | ||
|
d4df92aedc | ||
|
947ef27362 | ||
|
bf6d350a68 | ||
|
0a636257d4 | ||
|
ac9324da7a | ||
|
f6864d2ead | ||
|
c937b0ee01 | ||
|
d80f175c1a | ||
|
1b75bf9f8a | ||
|
bbcdd7711f | ||
|
81885eea47 | ||
|
d45abdd839 | ||
|
fc6f700bb5 | ||
|
66a21aad61 | ||
|
6b91f41a7c | ||
|
c14298db63 | ||
|
df8e3973f4 | ||
|
f9a63cb164 | ||
|
ff8cad8ae6 | ||
|
bab770045b | ||
|
f72b9c6ff4 | ||
|
b52be69e59 | ||
|
1500c41966 | ||
|
4ae41d8d20 | ||
|
9de66be60d | ||
|
4fcccf68cb | ||
|
872d464500 | ||
|
a1da3a504b | ||
|
5e76d87dfe | ||
|
75f6a6bf95 | ||
|
9b6b41b8ca | ||
|
9f8f461279 | ||
|
a56db25fed | ||
|
47d9afa176 | ||
|
d053a94be8 | ||
|
5edf194493 | ||
|
5b6c092b01 | ||
|
6446cfcff0 | ||
|
3500120c1f | ||
|
f1489baeb7 | ||
|
c83cd11615 | ||
|
0e91d185ca | ||
|
67364c1326 | ||
|
6a5ab68d56 | ||
|
61d9b9ff96 | ||
|
3682b31cbe | ||
|
52935b4e81 | ||
|
a61c7e6927 | ||
|
54ede03227 | ||
|
a8e7e18656 | ||
|
3e3324b06b | ||
|
44c27420e2 | ||
|
ba7e0d9657 | ||
|
878a9bea61 | ||
|
d6a10e6fbb | ||
|
b6514a8133 | ||
|
f792eecd1e | ||
|
343ce73b73 | ||
|
7e3c538af1 | ||
|
fa064e215b | ||
|
02f664dbf4 | ||
|
0efce7d2a8 | ||
|
13d9e27ef0 | ||
|
b8bb0326a8 | ||
|
690e294ece | ||
|
62d486be7c | ||
|
d270dcfa18 | ||
|
5f57e963d0 | ||
|
e050c46a53 | ||
|
d7de392b05 | ||
|
f8c1de5643 | ||
|
066d09538f | ||
|
43c933d7f8 | ||
|
1c3f79dfeb | ||
|
5ad99ac174 | ||
|
fd7870028f | ||
|
3f264d250b | ||
|
e0555f5bdf | ||
|
7475fb4bb4 | ||
|
8f59faffd1 | ||
|
c578271545 | ||
|
3279794487 | ||
|
28970fab19 | ||
|
febb444dcd | ||
|
6b563c50dd | ||
|
63c7f7d7d7 | ||
|
5d8db68b51 | ||
|
cd77197264 | ||
|
da1568ac03 | ||
|
b6b4451142 | ||
|
2f56276fbf | ||
|
72542b052d | ||
|
4116671cbf | ||
|
972be40f1f | ||
|
534189b735 | ||
|
e05aab7b6b | ||
|
0bed408b15 | ||
|
47e34e6f07 | ||
|
8d96707553 | ||
|
7db2fb7b93 | ||
|
a15a2a6811 | ||
|
5f6e1f0fe7 | ||
|
eb3bf49197 | ||
|
0866e8254d | ||
|
1a7a0a4d4b | ||
|
6de969a304 | ||
|
93cebf538e | ||
|
5f4fd97781 | ||
|
1208a8e369 | ||
|
c50346b341 | ||
|
fd67ff286e | ||
|
d6b6959e0e | ||
|
4191cae361 | ||
|
df8d4f964f | ||
|
d3ad7c4c99 | ||
|
bbc3789df1 | ||
|
05d61dfd83 | ||
|
daf16276b4 | ||
|
fa31053b42 | ||
|
d1118d6e14 | ||
|
e6e778f635 | ||
|
e8dbecb42c | ||
|
24c3c97633 | ||
|
258f2459fc | ||
|
dff2d3e632 | ||
|
fb7dbb4c28 | ||
|
05f02b46ed | ||
|
9a3de4160b | ||
|
789eac95fd | ||
|
de8292b1d4 | ||
|
88ccbb4804 | ||
|
a66d4b272a | ||
|
c6485cb937 | ||
|
a9d27c5ea0 | ||
|
4f291f0307 | ||
|
ae38954f50 | ||
|
7585a27356 | ||
|
78373e45c5 | ||
|
70f5af9444 | ||
|
1d8752bf0f | ||
|
f6147a6460 | ||
|
07b3445bf1 | ||
|
a5477bbfab | ||
|
e89a7747b8 | ||
|
1e8d2d5a4c | ||
|
7facb6a6fb | ||
|
b1f858f26b | ||
|
61b5dcb168 | ||
|
fe7f63632f | ||
|
76acc0edcd | ||
|
af7d5ccf7b | ||
|
04548babac | ||
|
84a55c5a72 | ||
|
26c31918f7 | ||
|
907f95c5bd | ||
|
7caf87236a | ||
|
f2e937f1cb | ||
|
80bcce941e | ||
|
6fbc102794 | ||
|
430c0a371a | ||
|
11ee73fc55 | ||
|
87063908ad | ||
|
199703cbff | ||
|
5d7f694f27 | ||
|
8e3dcd5a02 | ||
|
a4c66ebff5 | ||
|
3aa10d2a87 | ||
|
5e4f6feaf6 | ||
|
08b912f1ed | ||
|
6eccfe32b1 | ||
|
e06ebb4817 | ||
|
403124f9e2 | ||
|
1197f761a4 | ||
|
4ab7db99c1 | ||
|
30566abed8 | ||
|
3822e36b20 | ||
|
03c31f2100 | ||
|
577304f02a | ||
|
384c078d2e | ||
|
07e521c396 | ||
|
33f62804fd | ||
|
33c135a16f | ||
|
5951dd28af | ||
|
ae5193c24e | ||
|
f044345c23 | ||
|
25b886a7fd | ||
|
a42704eccc | ||
|
50cfc4a091 | ||
|
06c856fd67 | ||
|
29138814cc | ||
|
e6a2f54c4d | ||
|
bad70be095 | ||
|
84571a2b91 | ||
|
d529b2d0a0 | ||
|
976930db64 | ||
|
b2196565bd | ||
|
06cede988b | ||
|
a8d599dc00 | ||
|
efcd534bd2 | ||
|
ec24b1b19e | ||
|
09de69eca1 | ||
|
64b18d123a | ||
|
11582abc27 | ||
|
70698339f6 | ||
|
446616ee22 | ||
|
05829315f0 | ||
|
38e7f8b51b | ||
|
bf407c0c77 | ||
|
01596d3bc1 | ||
|
375928a0a6 | ||
|
e694156d72 | ||
|
7de777ec24 | ||
|
7e5c7d7c21 | ||
|
6e91e4a8fb | ||
|
305061cac0 | ||
|
bc582f782b | ||
|
29b0d713c9 | ||
|
a75f1bf9e7 | ||
|
6669714772 | ||
|
f53e77c011 | ||
|
3473acb12e | ||
|
0775bfd7ab | ||
|
b958379739 | ||
|
9d4dcad18b | ||
|
bf747d2dbb | ||
|
da1c36d03e | ||
|
ad8682bbca | ||
|
e312875f88 | ||
|
50be704a88 | ||
|
9525fcd633 | ||
|
be368a3d4a | ||
|
c26b26c2af | ||
|
52a90898ab | ||
|
405bb7aff7 | ||
|
1e9dbde88a | ||
|
e7b75dbcc3 | ||
|
9dfe692010 | ||
|
742e915661 | ||
|
86901970f2 | ||
|
494fa83886 | ||
|
1f17be3171 | ||
|
86643aa489 | ||
|
8d54c75092 | ||
|
912c99fc8e | ||
|
f3b59ca226 | ||
|
9f042be4a7 | ||
|
1a1890a52a | ||
|
8ade75f955 | ||
|
68e380d5a7 | ||
|
93257311a1 | ||
|
ce8d2156e8 | ||
|
60a883212f | ||
|
7b8b8632d3 | ||
|
e6c0689091 | ||
|
8f2b058398 | ||
|
3c59c247e2 | ||
|
ba5ee2c8f5 | ||
|
1b9920afa7 | ||
|
e919fdd63b | ||
|
48550041f9 | ||
|
d01a9f8bc4 | ||
|
1cbdeae78d | ||
|
32d04cdbbc | ||
|
69607b678f | ||
|
3ca4a120de | ||
|
b2f96f314c | ||
|
be55cc8cf0 | ||
|
df33e47bb4 | ||
|
dcebaf42ba | ||
|
94074a84d0 | ||
|
1807e385d7 | ||
|
04023b1f6a | ||
|
fd557e4a97 | ||
|
23c82999a8 | ||
|
3b3f0c3f61 | ||
|
322818287c | ||
|
a4a9922512 | ||
|
b0201df935 | ||
|
88fa7cfc6b | ||
|
fd18beeb5c | ||
|
bf25801d83 | ||
|
68b256aed3 | ||
|
248d340b71 | ||
|
dbf409ca20 | ||
|
d2b6b9ae82 | ||
|
cff9fdbd94 | ||
|
1530800640 | ||
|
0afda6b2c2 | ||
|
5a73dd4a2b | ||
|
ee8c48cc20 | ||
|
ad35fbc56a | ||
|
ed1c70b5b8 | ||
|
d328f2011f | ||
|
eba8cc5172 | ||
|
0df0555e75 | ||
|
9220e0baf1 | ||
|
281d52d944 | ||
|
b904343e14 | ||
|
b7fdc7396d | ||
|
7b8e2b7e09 | ||
|
80745c59de | ||
|
972ae49d47 | ||
|
78aaf6a665 | ||
|
c77fc8a32c | ||
|
fd00ae5f55 | ||
|
816f25011c | ||
|
717bf0ded2 | ||
|
c45b12b422 | ||
|
0344a368de | ||
|
0d9bd40775 | ||
|
cae59b4ae4 | ||
|
7405ecd046 | ||
|
efbe428d83 | ||
|
ef7262d477 | ||
|
b5dfa2834d | ||
|
8322cd6586 | ||
|
e41dc09252 | ||
|
6c05f5ddd9 | ||
|
fb7fdb9426 | ||
|
30b075c4a5 | ||
|
ccc2446863 | ||
|
3665a7d09b | ||
|
d9d863e153 | ||
|
916a34a982 | ||
|
43e6db7e72 | ||
|
f66ff1afd2 | ||
|
67b65a379c | ||
|
4adb6f4f7b | ||
|
bbd8e82a7f | ||
|
3b4ac42ff8 | ||
|
3382d1e0a6 | ||
|
a8c1c466a1 | ||
|
eabfff133d | ||
|
41f998fe5a | ||
|
43378265bf | ||
|
ac05fa05b5 | ||
|
cfc8628092 | ||
|
9c30407f7f | ||
|
08730fc9a6 | ||
|
361f24eac7 | ||
|
d95350e97a | ||
|
5b117cf53a | ||
|
d35f113d02 | ||
|
d192a71e24 | ||
|
e574a2adc6 | ||
|
f801709a68 | ||
|
8b2e6865e5 | ||
|
2b4274afc4 | ||
|
c653618a91 | ||
|
8c33b3e9c9 | ||
|
de2c055770 | ||
|
82a0188946 | ||
|
8d1130a89e | ||
|
4621ee0691 | ||
|
cec12872c4 | ||
|
9a57c16c97 | ||
|
a91e414f84 | ||
|
b6ee7dcb22 | ||
|
378e28e301 | ||
|
efa30c13f2 | ||
|
c40bcc74d9 | ||
|
1efa3f9e10 | ||
|
4b7f1eba64 | ||
|
1fa7c1c4c4 | ||
|
df9901a45d | ||
|
fda6a1e6fe | ||
|
0e3b7020b8 | ||
|
7fde9a879d | ||
|
90a41b68ad | ||
|
b5b5f9e520 | ||
|
9fc5812260 | ||
|
bb87ebd809 | ||
|
f24c2e1952 | ||
|
53a8b1a204 | ||
|
6c7d641dc9 | ||
|
3a7466663c | ||
|
8ca54e486c | ||
|
7d279feb8f | ||
|
513083b598 | ||
|
662b6da59d | ||
|
8e77c078d6 | ||
|
64506d2ed7 | ||
|
aadace2751 | ||
|
34d3a2e0f3 | ||
|
2452f06b16 | ||
|
c2c4192c1b | ||
|
42943c2391 | ||
|
16f2f37682 | ||
|
a453cc24f4 | ||
|
f8bca185b9 | ||
|
8d2c518c0b | ||
|
3b7d8ee362 | ||
|
07a1c07b7b | ||
|
2072e7e7a9 | ||
|
e167e9e61f | ||
|
39edd96373 | ||
|
0b12b34928 | ||
|
2b7ae09319 | ||
|
31b4957eea | ||
|
14889c340f | ||
|
a599ae80aa | ||
|
f5c8f6fb61 | ||
|
3a69e0e1d1 | ||
|
144938cf22 | ||
|
315fd78199 | ||
|
863aa2a74a | ||
|
25b9371026 | ||
|
e4f52d1c27 | ||
|
26ff18d101 | ||
|
44af96cd2d | ||
|
7cdcc98395 | ||
|
2543651766 | ||
|
0ab73db415 | ||
|
0fdc829d32 | ||
|
0b814c1d2b | ||
|
1b8c24f337 | ||
|
97918069d8 | ||
|
6e3869c19f | ||
|
b5b8051a23 | ||
|
a2d5da4831 | ||
|
23eabdedab | ||
|
6f598cc435 | ||
|
615d2c0d6d | ||
|
4ac61acb3a | ||
|
786275e719 | ||
|
c5f5e0ae65 | ||
|
56eac4dd7a | ||
|
0dd6c95b3f | ||
|
8014071440 | ||
|
1c87cc86e3 | ||
|
7fbd77715f | ||
|
e193098543 | ||
|
4f456b8c0d | ||
|
b80b13fd2f | ||
|
f16fbd5e96 | ||
|
6869f2e1b3 | ||
|
1adc07f0ff | ||
|
9c1e16eaaa | ||
|
bd0a7f9da1 | ||
|
75ce593891 | ||
|
35c41e00ee | ||
|
8ffb12eb35 | ||
|
a4067bc197 | ||
|
e4cc9b12ec | ||
|
4f182c746b | ||
|
c0fc538ae4 | ||
|
8e3c63b7f9 | ||
|
b0059c54da | ||
|
0b54af4f91 | ||
|
4abf9d9ebd | ||
|
1273651fcf | ||
|
7b106e34ba | ||
|
951583a8ca | ||
|
bcf885c807 | ||
|
8cf0a52bec | ||
|
7d0d107b17 | ||
|
3d0b3be6c0 | ||
|
6bc523c618 | ||
|
297b1e5a06 | ||
|
f6163f1e29 | ||
|
073a7f6695 | ||
|
52a508dfe2 | ||
|
25b512d64d | ||
|
775c6f7640 | ||
|
d3e4bbb9c7 | ||
|
deb500f6c8 | ||
|
b28be1254c | ||
|
b0ec96cee2 | ||
|
6c97801617 | ||
|
ffedcd4945 | ||
|
eee511f1df | ||
|
c90769edd6 | ||
|
131c0c6a83 | ||
|
f2dfb88b2a | ||
|
56e7a3c65d | ||
|
504280af60 | ||
|
865df5af57 | ||
|
d5b689e7af | ||
|
089003b7f6 | ||
|
1e73bad86e | ||
|
a62dc27d23 | ||
|
b223627d81 | ||
|
c0ec51afe0 | ||
|
87855248f1 | ||
|
9e6b619ad0 | ||
|
5c27539f91 | ||
|
b34b1d8525 | ||
|
2a18f064ce | ||
|
a0aca73851 | ||
|
608d142aaa | ||
|
00dbb73752 | ||
|
f3319839c4 | ||
|
b68b800189 | ||
|
18dd9d0104 | ||
|
43135e6e46 | ||
|
e7e1e1ebf5 | ||
|
c609f474a9 | ||
|
a76aaa1442 | ||
|
0de9f43b87 | ||
|
3e768ed9c0 | ||
|
6d3a825dbf | ||
|
a7eb2329de | ||
|
9244496c6f | ||
|
5431f41508 | ||
|
19374d2636 | ||
|
81aadb99a6 | ||
|
de545c09d0 | ||
|
841ad48a37 | ||
|
5cf2e760b6 | ||
|
19d77e0c33 | ||
|
d6fb5a7c51 | ||
|
6de1b75da4 | ||
|
89f1396fd1 | ||
|
71f7580891 | ||
|
dd5067f391 | ||
|
952f3c7b73 | ||
|
c50e7c604c | ||
|
83e085b7a3 | ||
|
bad560bb69 | ||
|
f480ba1169 | ||
|
bd76cd035c | ||
|
2b48a6a89f | ||
|
7f1455601d | ||
|
89e07bf2c7 | ||
|
6a9ffff696 | ||
|
2214cb9bc1 | ||
|
e87521a989 | ||
|
e53e6af0c1 | ||
|
dc4029a5c2 | ||
|
3e60d65111 | ||
|
1ac84da26d | ||
|
03ebc1d245 | ||
|
384b1c7d7d | ||
|
97f1da43ae | ||
|
057aa1f617 | ||
|
32d1b2882b | ||
|
bc64ee962f | ||
|
8bfcc2683f | ||
|
e15bffe466 | ||
|
f4a106d4b5 | ||
|
2684b4dc19 | ||
|
5251e64f48 | ||
|
7b873221e6 | ||
|
eef5ccc61f | ||
|
cf55ca5b5c | ||
|
e7b2043dd2 | ||
|
330ca2f7c7 | ||
|
0441036f9e | ||
|
e9faf54828 | ||
|
5b787b1911 | ||
|
2832dc011c | ||
|
c65d3e6206 | ||
|
718478d95d | ||
|
910f68a5d4 | ||
|
6f50ca38bb | ||
|
b64d76a3b8 | ||
|
92b1f834d1 | ||
|
5ebd6507e3 | ||
|
0468306245 | ||
|
c823931376 | ||
|
52cb2c6f42 | ||
|
6ed5383245 | ||
|
5a6e1d3f03 | ||
|
9f211b4283 | ||
|
0a5fc5d663 | ||
|
1adb184d42 | ||
|
9276b0d301 | ||
|
7a77f45f79 | ||
|
e4ffacd0d8 | ||
|
f7f9dfcab6 | ||
|
5fb436d5bb | ||
|
315c4f6b20 | ||
|
bdd7ca54cd | ||
|
07466260e2 | ||
|
4969cfc647 | ||
|
08fecb2e43 | ||
|
7f941f990a | ||
|
0397da8e08 | ||
|
bb846bdc98 | ||
|
1c9c0568e0 | ||
|
9dcc03f47d | ||
|
bb618860df | ||
|
0cbd50c975 | ||
|
d95a88c3f1 | ||
|
72a2bff592 | ||
|
3b9b206b5f | ||
|
b1f271a4ec | ||
|
0314e487e4 | ||
|
096992fac7 | ||
|
873064894b | ||
|
927cc8fa2a | ||
|
2712a8c712 | ||
|
ebebd0fcef | ||
|
0e76d1fdf3 | ||
|
96c69639ee | ||
|
5f4fa55598 | ||
|
a3043159f3 | ||
|
cb33b20f75 | ||
|
e4be54eb3e | ||
|
8d70618ffc | ||
|
760d66430e | ||
|
c84c9e7bb3 | ||
|
b73230385c | ||
|
895d3d17ee | ||
|
4ee3ddcd33 | ||
|
ce9178abaa | ||
|
b2bc3bcc84 | ||
|
c426b3d98a | ||
|
ddd71b75f7 | ||
|
ebe77d7e3b | ||
|
b0baafc4da | ||
|
7c48629d49 | ||
|
d4a212a099 | ||
|
eab03fdb72 | ||
|
f7cc9ef6f5 | ||
|
54bc5cfa2d |
83
.gitignore
vendored
Normal file
83
.gitignore
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Ignore other VCSs.
|
||||
.svn/
|
||||
|
||||
# Ignore common compiled artifacts.
|
||||
*~
|
||||
*.dwo
|
||||
*.o
|
||||
lib*.a
|
||||
/breakpad.pc
|
||||
/breakpad-client.pc
|
||||
/src/client/linux/linux_client_unittest_shlib
|
||||
/src/client/linux/linux_dumper_unittest_helper
|
||||
/src/processor/microdump_stackwalk
|
||||
/src/processor/minidump_dump
|
||||
/src/processor/minidump_stackwalk
|
||||
/src/tools/linux/core2md/core2md
|
||||
/src/tools/linux/dump_syms/dump_syms
|
||||
/src/tools/linux/md2core/minidump-2-core
|
||||
/src/tools/linux/symupload/minidump_upload
|
||||
/src/tools/linux/symupload/sym_upload
|
||||
/src/tools/mac/dump_syms/dump_syms
|
||||
|
||||
# Ignore autotools generated artifacts.
|
||||
.deps
|
||||
.dirstamp
|
||||
autom4te.cache/
|
||||
/config.cache
|
||||
config.h
|
||||
/config.log
|
||||
/config.status
|
||||
/Makefile
|
||||
stamp-h1
|
||||
|
||||
# Ignore GYP generated Visual Studio artifacts.
|
||||
*.filters
|
||||
*.sdf
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*.vcxproj
|
||||
|
||||
# Ignore GYP generated Makefiles
|
||||
src/Makefile
|
||||
*.Makefile
|
||||
*.target.mk
|
||||
|
||||
# Ignore compiled Python files.
|
||||
*.pyc
|
||||
|
||||
# Ignore directories gclient syncs.
|
||||
src/testing
|
||||
src/third_party/glog
|
||||
src/third_party/lss
|
||||
src/third_party/protobuf
|
||||
src/tools/gyp
|
26
.travis.yml
Normal file
26
.travis.yml
Normal file
@ -0,0 +1,26 @@
|
||||
# Travis build integration.
|
||||
# https://docs.travis-ci.com/
|
||||
language: cpp
|
||||
# TODO: add a clang build as well.
|
||||
compiler:
|
||||
- gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-4.8
|
||||
- g++-4.8
|
||||
# Travis sets CC/CXX to the system toolchain based on the `compiler`
|
||||
# selection. If clang is added, this should move to be set inside the
|
||||
# matrix.
|
||||
env:
|
||||
- USE_CC=gcc-4.8 USE_CXX=g++-4.8
|
||||
before_install: ./scripts/travis-checkout.sh
|
||||
script: ./scripts/travis-build.sh
|
||||
# TODO: add mac support
|
||||
os:
|
||||
- linux
|
||||
notifications:
|
||||
email:
|
||||
- google-breakpad-dev@googlegroups.com
|
75
DEPS
Normal file
75
DEPS
Normal file
@ -0,0 +1,75 @@
|
||||
# Copyright 2010 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This is used to mimic the svn:externals mechanism for gclient (both Git and
|
||||
# SVN) based checkouts of Breakpad. As such, its use is entirely optional. If
|
||||
# using a manually managed SVN checkout as opposed to a gclient managed checkout
|
||||
# you can still use the hooks mechanism for generating project files by calling
|
||||
# 'gclient runhooks' rather than 'gclient sync'.
|
||||
|
||||
deps = {
|
||||
# Logging code.
|
||||
"src/src/third_party/glog":
|
||||
"https://github.com/google/glog.git" +
|
||||
"@v0.3.4",
|
||||
|
||||
# Testing libraries and utilities.
|
||||
"src/src/testing":
|
||||
"https://github.com/google/googlemock.git" +
|
||||
"@release-1.7.0",
|
||||
"src/src/testing/gtest":
|
||||
"https://github.com/google/googletest.git" +
|
||||
"@release-1.7.0",
|
||||
|
||||
# Protobuf.
|
||||
"src/src/third_party/protobuf/protobuf":
|
||||
"https://github.com/google/protobuf.git" +
|
||||
"@cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac",
|
||||
|
||||
# GYP project generator.
|
||||
"src/src/tools/gyp":
|
||||
"https://chromium.googlesource.com/external/gyp/" +
|
||||
"@e8ab0833a42691cd2184bd4c45d779e43821d3e0",
|
||||
|
||||
# Linux syscall support.
|
||||
"src/src/third_party/lss":
|
||||
"https://chromium.googlesource.com/linux-syscall-support/" +
|
||||
"@9292030109847793f7a6689adac1ddafb412fe14"
|
||||
}
|
||||
|
||||
hooks = [
|
||||
{
|
||||
# TODO(chrisha): Fix the GYP files so that they work without
|
||||
# --no-circular-check.
|
||||
"pattern": ".",
|
||||
"action": ["python",
|
||||
"src/src/tools/gyp/gyp_main.py",
|
||||
"--no-circular-check",
|
||||
"src/src/client/windows/breakpad_client.gyp"],
|
||||
},
|
||||
]
|
210
INSTALL
210
INSTALL
@ -1,19 +1,25 @@
|
||||
Installation Instructions
|
||||
*************************
|
||||
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||
2006 Free Software Foundation, Inc.
|
||||
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
This file is free documentation; the Free Software Foundation gives
|
||||
unlimited permission to copy, distribute and modify it.
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. This file is offered as-is,
|
||||
without warranty of any kind.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
configure, build, and install this package. The following
|
||||
more-detailed instructions are generic; see the `README' file for
|
||||
instructions specific to this package.
|
||||
instructions specific to this package. Some packages provide this
|
||||
`INSTALL' file but do not implement all of the features documented
|
||||
below. The lack of an optional feature in a given package is not
|
||||
necessarily a bug. More recommendations for GNU packages can be found
|
||||
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
@ -42,7 +48,7 @@ may remove or edit it.
|
||||
you want to change it or regenerate `configure' using a newer version
|
||||
of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system.
|
||||
@ -53,12 +59,22 @@ The simplest way to compile this package is:
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package.
|
||||
the package, generally using the just-built uninstalled binaries.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
documentation. When installing into a prefix owned by root, it is
|
||||
recommended that the package be configured and built as a regular
|
||||
user, and only the `make install' phase executed with root
|
||||
privileges.
|
||||
|
||||
5. You can remove the program binaries and object files from the
|
||||
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||
this time using the binaries in their final installed location.
|
||||
This target does not install anything. Running this target as a
|
||||
regular user, particularly if the prior `make install' required
|
||||
root privileges, verifies that the installation completed
|
||||
correctly.
|
||||
|
||||
6. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
@ -67,12 +83,22 @@ The simplest way to compile this package is:
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
7. Often, you can also type `make uninstall' to remove the installed
|
||||
files again. In practice, not all packages have tested that
|
||||
uninstallation works correctly, even though it is required by the
|
||||
GNU Coding Standards.
|
||||
|
||||
8. Some packages, particularly those that use Automake, provide `make
|
||||
distcheck', which can by used by developers to test that all other
|
||||
targets like `make install' and `make uninstall' work correctly.
|
||||
This target is generally not run by end users.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that the
|
||||
`configure' script does not know about. Run `./configure --help' for
|
||||
details on some of the pertinent environment variables.
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. Run `./configure --help'
|
||||
for details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
@ -85,25 +111,41 @@ is an example:
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
source code in the directory that `configure' is in and in `..'. This
|
||||
is known as a "VPATH" build.
|
||||
|
||||
With a non-GNU `make', it is safer to compile the package for one
|
||||
architecture at a time in the source code directory. After you have
|
||||
installed the package for one architecture, use `make distclean' before
|
||||
reconfiguring for another architecture.
|
||||
|
||||
On MacOS X 10.5 and later systems, you can create libraries and
|
||||
executables that work on multiple system types--known as "fat" or
|
||||
"universal" binaries--by specifying multiple `-arch' options to the
|
||||
compiler but only a single `-arch' option to the preprocessor. Like
|
||||
this:
|
||||
|
||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CPP="gcc -E" CXXCPP="g++ -E"
|
||||
|
||||
This is not guaranteed to produce working output in all cases, you
|
||||
may have to build one architecture at a time and combine the results
|
||||
using the `lipo' tool if you have problems.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' installs the package's commands under
|
||||
By default, `make install' installs the package's commands under
|
||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||
can specify an installation prefix other than `/usr/local' by giving
|
||||
`configure' the option `--prefix=PREFIX'.
|
||||
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||
absolute file name.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
@ -114,16 +156,47 @@ Documentation and other data files still use the regular prefix.
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=DIR' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them.
|
||||
you can set and what kinds of files go in them. In general, the
|
||||
default for these options is expressed in terms of `${prefix}', so that
|
||||
specifying just `--prefix' will affect all of the other directory
|
||||
specifications that were not explicitly provided.
|
||||
|
||||
The most portable way to affect installation locations is to pass the
|
||||
correct locations to `configure'; however, many packages provide one or
|
||||
both of the following shortcuts of passing variable assignments to the
|
||||
`make install' command line to change installation locations without
|
||||
having to reconfigure or recompile.
|
||||
|
||||
The first method involves providing an override variable for each
|
||||
affected directory. For example, `make install
|
||||
prefix=/alternate/directory' will choose an alternate location for all
|
||||
directory configuration variables that were expressed in terms of
|
||||
`${prefix}'. Any directories that were specified during `configure',
|
||||
but not in terms of `${prefix}', must each be overridden at install
|
||||
time for the entire installation to be relocated. The approach of
|
||||
makefile variable overrides for each directory variable is required by
|
||||
the GNU Coding Standards, and ideally causes no recompilation.
|
||||
However, some platforms have known limitations with the semantics of
|
||||
shared libraries that end up requiring recompilation when using this
|
||||
method, particularly noticeable in packages that use GNU Libtool.
|
||||
|
||||
The second method involves providing the `DESTDIR' variable. For
|
||||
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||
`/alternate/directory' before all installation names. The approach of
|
||||
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||
does not work on platforms that have drive letters. On the other hand,
|
||||
it does better at avoiding recompilation issues, and works well even
|
||||
when some directory options were not specified in terms of `${prefix}'
|
||||
at `configure' time.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
@ -135,14 +208,58 @@ find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Some packages offer the ability to configure how verbose the
|
||||
execution of `make' will be. For these packages, running `./configure
|
||||
--enable-silent-rules' sets the default to minimal output, which can be
|
||||
overridden with `make V=1'; while running `./configure
|
||||
--disable-silent-rules' sets the default to verbose, which can be
|
||||
overridden with `make V=0'.
|
||||
|
||||
Particular systems
|
||||
==================
|
||||
|
||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||
CC is not installed, it is recommended to use the following options in
|
||||
order to use an ANSI C compiler:
|
||||
|
||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||
|
||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||
|
||||
HP-UX `make' updates targets which have the same time stamps as
|
||||
their prerequisites, which makes it generally unusable when shipped
|
||||
generated files such as `configure' are involved. Use GNU `make'
|
||||
instead.
|
||||
|
||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||
to try
|
||||
|
||||
./configure CC="cc"
|
||||
|
||||
and if that doesn't work, try
|
||||
|
||||
./configure CC="cc -nodtk"
|
||||
|
||||
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||
directory contains several dysfunctional programs; working variants of
|
||||
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||
in your `PATH', put it _after_ `/usr/bin'.
|
||||
|
||||
On Haiku, software installed for all users goes in `/boot/common',
|
||||
not `/usr/local'. It is recommended to use the following options:
|
||||
|
||||
./configure --prefix=/boot/common
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out automatically,
|
||||
but needs to determine by the type of machine the package will run on.
|
||||
Usually, assuming the package is built to be run on the _same_
|
||||
architectures, `configure' can figure that out, but if it prints a
|
||||
message saying it cannot guess the machine type, give it the
|
||||
There may be some features `configure' cannot figure out
|
||||
automatically, but needs to determine by the type of machine the package
|
||||
will run on. Usually, assuming the package is built to be run on the
|
||||
_same_ architectures, `configure' can figure that out, but if it prints
|
||||
a message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
@ -150,7 +267,8 @@ type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS KERNEL-OS
|
||||
OS
|
||||
KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
@ -168,9 +286,9 @@ eventually be run) with `--host=TYPE'.
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share, you
|
||||
can create a site shell script called `config.site' that gives default
|
||||
values for variables like `CC', `cache_file', and `prefix'.
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
@ -179,7 +297,7 @@ A warning: not all `configure' scripts look for a site script.
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
@ -191,18 +309,27 @@ causes the specified `gcc' to be used as the C compiler (unless it is
|
||||
overridden in the site shell script).
|
||||
|
||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||
an Autoconf limitation. Until the limitation is lifted, you can use
|
||||
this workaround:
|
||||
|
||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it operates.
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
Print a summary of all of the options to `configure', and exit.
|
||||
|
||||
`--help=short'
|
||||
`--help=recursive'
|
||||
Print a summary of the options unique to this package's
|
||||
`configure', and exit. The `short' variant lists options used
|
||||
only in the top level, while the `recursive' variant lists options
|
||||
also present in any nested packages.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
@ -229,6 +356,15 @@ an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--prefix=DIR'
|
||||
Use DIR as the installation prefix. *note Installation Names::
|
||||
for more details, including other options available for fine-tuning
|
||||
the installation locations.
|
||||
|
||||
`--no-create'
|
||||
`-n'
|
||||
Run the configure checks, but stop before creating any output
|
||||
files.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
||||
|
@ -26,3 +26,25 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Copyright 2001-2004 Unicode, Inc.
|
||||
|
||||
Disclaimer
|
||||
|
||||
This source code is provided as is by Unicode, Inc. No claims are
|
||||
made as to fitness for any particular purpose. No warranties of any
|
||||
kind are expressed or implied. The recipient agrees to determine
|
||||
applicability of information provided. If this file has been
|
||||
purchased on magnetic or optical media from Unicode, Inc., the
|
||||
sole remedy for any claim will be exchange of defective media
|
||||
within 90 days of receipt.
|
||||
|
||||
Limitations on Rights to Redistribute This Code
|
||||
|
||||
Unicode, Inc. hereby grants the right to freely use the information
|
||||
supplied in this file in the creation of products supporting the
|
||||
Unicode Standard, and to make copies of this file in any form
|
||||
for internal or external distribution as long as this notice
|
||||
remains attached.
|
1388
Makefile.am
1388
Makefile.am
File diff suppressed because it is too large
Load Diff
8327
Makefile.in
8327
Makefile.in
File diff suppressed because it is too large
Load Diff
2
README
2
README
@ -1,2 +0,0 @@
|
||||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
139
README.ANDROID
Normal file
139
README.ANDROID
Normal file
@ -0,0 +1,139 @@
|
||||
Google Breakpad for Android
|
||||
===========================
|
||||
|
||||
This document explains how to use the Google Breakpad client library
|
||||
on Android, and later generate valid stack traces from the minidumps
|
||||
it generates.
|
||||
|
||||
This release supports ARM, x86 and MIPS based Android systems.
|
||||
This release requires NDK release r11c or higher.
|
||||
|
||||
I. Building the client library:
|
||||
===============================
|
||||
|
||||
The Android client is built as a static library that you can
|
||||
link into your own Android native code. There are two ways to
|
||||
build it:
|
||||
|
||||
I.1. Building with ndk-build:
|
||||
-----------------------------
|
||||
|
||||
If you're using the ndk-build build system, you can follow
|
||||
these simple steps:
|
||||
|
||||
1/ Include android/google_breakpad/Android.mk from your own
|
||||
project's Android.mk
|
||||
|
||||
This can be done either directly, or using ndk-build's
|
||||
import-module feature.
|
||||
|
||||
2/ Link the library to one of your modules by using:
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += breakpad_client
|
||||
|
||||
NOTE: The client library requires a C++ STL implementation,
|
||||
which you can select with APP_STL in your Application.mk
|
||||
|
||||
It has been tested succesfully with both STLport and GNU libstdc++
|
||||
|
||||
|
||||
I.2. Building with a standalone Android toolchain:
|
||||
--------------------------------------------------
|
||||
|
||||
All you need to do is configure your build with the right 'host'
|
||||
value, and disable the processor and tools, as in:
|
||||
|
||||
$GOOGLE_BREAKPAD_PATH/configure --host=arm-linux-androideabi \
|
||||
--disable-processor \
|
||||
--disable-tools
|
||||
make -j4
|
||||
|
||||
The library will be under src/client/linux/libbreakpad_client.a
|
||||
|
||||
You can also use 'make check' to run the test suite on a connected
|
||||
Android device. This requires the Android 'adb' tool to be in your
|
||||
path.
|
||||
|
||||
II. Using the client library in Android:
|
||||
========================================
|
||||
|
||||
The usage instructions are very similar to the Linux ones that are
|
||||
found at https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/linux_starter_guide.md
|
||||
|
||||
1/ You need to include "client/linux/handler/exception_handler.h" from a C++
|
||||
source file.
|
||||
|
||||
2/ If you're not using ndk-build, you also need to:
|
||||
|
||||
- add the following to your compiler include search paths:
|
||||
$GOOGLE_BREAKPAD_PATH/src
|
||||
$GOOGLE_BREAKPAD_PATH/src/common/android/include
|
||||
|
||||
- add -llog to your linker flags
|
||||
|
||||
Note that ndk-build does that for your automatically.
|
||||
|
||||
3/ Keep in mind that there is no /tmp directory on Android.
|
||||
|
||||
If you use the library from a regular Android applications, specify a
|
||||
path under your app-specific storage directory. An alternative is to
|
||||
store them on the SDCard, but this requires a specific permission.
|
||||
|
||||
For a concrete example, see the sample test application under
|
||||
android/sample_app. See its README for more information.
|
||||
|
||||
|
||||
III. Getting a stack trace on the host:
|
||||
=======================================
|
||||
|
||||
This process is similar to other platforms, but here's a quick example:
|
||||
|
||||
1/ Retrieve the minidumps on your development machine.
|
||||
|
||||
2/ Dump the symbols for your native libraries with the 'dump_syms' tool.
|
||||
This first requires building the host version of Google Breakpad, then
|
||||
calling:
|
||||
|
||||
dump_syms $PROJECT_PATH/obj/local/$ABI/libfoo.so > libfoo.so.sym
|
||||
|
||||
3/ Create the symbol directory hierarchy.
|
||||
|
||||
The first line of the generated libfoo.so.sym will have a "MODULE"
|
||||
entry that carries a hexadecimal version number, e.g.:
|
||||
|
||||
MODULE Linux arm D51B4A5504974FA6ECC1869CAEE3603B0 test_google_breakpad
|
||||
|
||||
Note: The second field could be either 'Linux' or 'Android'.
|
||||
|
||||
Extract the version number, and a 'symbol' directory, for example:
|
||||
|
||||
$PROJECT_PATH/symbols/libfoo.so/$VERSION/
|
||||
|
||||
Copy/Move your libfoo.sym file there.
|
||||
|
||||
4/ Invoke minidump_stackwalk to create the stack trace:
|
||||
|
||||
minidump_stackwalk $MINIDUMP_FILE $PROJECT_PATH/symbols
|
||||
|
||||
Note that various helper scripts can be found on the web to automate these
|
||||
steps.
|
||||
|
||||
IV. Verifying the Android build library:
|
||||
========================================
|
||||
|
||||
If you modify Google Breakpad and want to check that it still works correctly
|
||||
on Android, please run the android/run-checks.sh script which will do all
|
||||
necessary verifications for you. This includes:
|
||||
|
||||
- Rebuilding the full host binaries.
|
||||
- Rebuilding the full Android binaries with configure/make.
|
||||
- Rebuilding the client library unit tests, and running them on a device.
|
||||
- Rebuilding the client library with ndk-build.
|
||||
- Building, installing and running a test crasher program on a device.
|
||||
- Extracting the corresponding minidump, dumping the test program symbols
|
||||
and generating a stack trace.
|
||||
- Checking the generated stack trace for valid source locations.
|
||||
|
||||
For more details, please run:
|
||||
|
||||
android/run-checks.sh --help-all
|
80
README.md
Normal file
80
README.md
Normal file
@ -0,0 +1,80 @@
|
||||
# Breakpad
|
||||
|
||||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
||||
|
||||
* [Homepage](https://chromium.googlesource.com/breakpad/breakpad/)
|
||||
* [Documentation](https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/)
|
||||
* [Bugs](https://bugs.chromium.org/p/google-breakpad/)
|
||||
* Discussion/Questions: [google-breakpad-discuss@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-discuss)
|
||||
* Developer/Reviews: [google-breakpad-dev@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-dev)
|
||||
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad)
|
||||
|
||||
## Getting started (from master)
|
||||
|
||||
1. First, [download depot_tools](http://dev.chromium.org/developers/how-tos/install-depot-tools)
|
||||
and ensure that they're in your `PATH`.
|
||||
|
||||
2. Create a new directory for checking out the source code (it must be named
|
||||
breakpad).
|
||||
|
||||
```sh
|
||||
mkdir breakpad && cd breakpad
|
||||
```
|
||||
|
||||
3. Run the `fetch` tool from depot_tools to download all the source repos.
|
||||
|
||||
```sh
|
||||
fetch breakpad
|
||||
cd src
|
||||
```
|
||||
|
||||
4. Build the source.
|
||||
|
||||
```sh
|
||||
./configure && make
|
||||
```
|
||||
|
||||
You can also cd to another directory and run configure from there to build
|
||||
outside the source tree.
|
||||
|
||||
This will build the processor tools (`src/processor/minidump_stackwalk`,
|
||||
`src/processor/minidump_dump`, etc), and when building on Linux it will
|
||||
also build the client libraries and some tools
|
||||
(`src/tools/linux/dump_syms/dump_syms`,
|
||||
`src/tools/linux/md2core/minidump-2-core`, etc).
|
||||
|
||||
5. Optionally, run tests.
|
||||
|
||||
```sh
|
||||
make check
|
||||
```
|
||||
|
||||
6. Optionally, install the built libraries
|
||||
|
||||
```sh
|
||||
make install
|
||||
```
|
||||
|
||||
If you need to reconfigure your build be sure to run `make distclean` first.
|
||||
|
||||
To update an existing checkout to a newer revision, you can
|
||||
`git pull` as usual, but then you should run `gclient sync` to ensure that the
|
||||
dependent repos are up-to-date.
|
||||
|
||||
## To request change review
|
||||
|
||||
1. Follow the steps above to get the source and build it.
|
||||
|
||||
2. Make changes. Build and test your changes.
|
||||
For core code like processor use methods above.
|
||||
For linux/mac/windows, there are test targets in each project file.
|
||||
|
||||
3. Commit your changes to your local repo and upload them to the server.
|
||||
http://dev.chromium.org/developers/contributing-code
|
||||
e.g. `git commit ... && git cl upload ...`
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
4. At https://codereview.chromium.org/ you'll find your issue listed; click on
|
||||
it, and select Publish+Mail, and enter in the code reviewer and CC
|
||||
google-breakpad-dev@googlegroups.com
|
7658
aclocal.m4
vendored
7658
aclocal.m4
vendored
File diff suppressed because it is too large
Load Diff
372
android/common-functions.sh
Executable file
372
android/common-functions.sh
Executable file
@ -0,0 +1,372 @@
|
||||
# Copyright (c) 2012 Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Collection of common shell functions for 'run-checks.sh' et 'test-shell.sh'
|
||||
|
||||
# All internal variables and functions use an underscore as a prefix
|
||||
# (e.g. _VERBOSE, _ALL_CLEANUPS, etc..).
|
||||
|
||||
# Sanitize the environment
|
||||
export LANG=C
|
||||
export LC_ALL=C
|
||||
|
||||
if [ "$BASH_VERSION" ]; then
|
||||
set -o posix
|
||||
fi
|
||||
|
||||
# Utility functions
|
||||
|
||||
_ALL_CLEANUPS=
|
||||
|
||||
# Register a function to be called when the script exits, even in case of
|
||||
# Ctrl-C, logout, etc.
|
||||
# $1: function name.
|
||||
atexit () {
|
||||
if [ -z "$_ALL_CLEANUPS" ]; then
|
||||
_ALL_CLEANUPS=$1
|
||||
# Ensure a clean exit when the script is:
|
||||
# - Exiting normally (EXIT)
|
||||
# - Interrupted by Ctrl-C (INT)
|
||||
# - Interrupted by log out (HUP)
|
||||
# - Being asked to quit nicely (TERM)
|
||||
# - Being asked to quit and dump core (QUIT)
|
||||
trap "_exit_cleanups \$?" EXIT INT HUP QUIT TERM
|
||||
else
|
||||
_ALL_CLEANUPS="$_ALL_CLEANUPS $1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Called on exit if at least one function was registered with atexit
|
||||
# $1: final exit status code
|
||||
_exit_cleanups () {
|
||||
local CLEANUP CLEANUPS
|
||||
# Ignore calls to atexit during cleanups
|
||||
CLEANUPS=$_ALL_CLEANUPS
|
||||
_ALL_CLEANUPS=
|
||||
for CLEANUP in $CLEANUPS; do
|
||||
($CLEANUP)
|
||||
done
|
||||
exit "$@"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# Dump a panic message then exit.
|
||||
# $1+: message
|
||||
panic () {
|
||||
echo "ERROR: $@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If the previous command failed, dump a panic message then exit.
|
||||
# $1+: message.
|
||||
fail_panic () {
|
||||
if [ $? != 0 ]; then
|
||||
panic "$@"
|
||||
fi;
|
||||
}
|
||||
|
||||
_VERBOSE=0
|
||||
|
||||
# Increase verbosity for dump/log/run/run2 functions
|
||||
increase_verbosity () {
|
||||
_VERBOSE=$(( $_VERBOSE + 1 ))
|
||||
}
|
||||
|
||||
# Decrease verbosity
|
||||
decrease_verbosity () {
|
||||
_VERBOSE=$(( $_VERBOSE - 1 ))
|
||||
}
|
||||
|
||||
# Returns success iff verbosity level is higher than a specific value
|
||||
# $1: verbosity level
|
||||
verbosity_is_higher_than () {
|
||||
[ "$_VERBOSE" -gt "$1" ]
|
||||
}
|
||||
|
||||
# Returns success iff verbosity level is lower than a specific value
|
||||
# $1: verbosity level
|
||||
verbosity_is_lower_than () {
|
||||
[ "$_VERBOSE" -le "$1" ]
|
||||
}
|
||||
|
||||
# Dump message to stdout, unless verbosity is < 0, i.e. --quiet was called
|
||||
# $1+: message
|
||||
dump () {
|
||||
if [ "$_VERBOSE" -ge 0 ]; then
|
||||
printf "%s\n" "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
# If --verbose was used, dump a message to stdout.
|
||||
# $1+: message
|
||||
log () {
|
||||
if [ "$_VERBOSE" -ge 1 ]; then
|
||||
printf "%s\n" "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
_RUN_LOG=
|
||||
|
||||
# Set a run log file that can be used to collect the output of commands that
|
||||
# are not displayed.
|
||||
set_run_log () {
|
||||
_RUN_LOG=$1
|
||||
}
|
||||
|
||||
# Run a command. Output depends on $_VERBOSE:
|
||||
# $_VERBOSE <= 0: Run command, store output into the run log
|
||||
# $_VERBOSE >= 1: Dump command, run it, output goest to stdout
|
||||
# Note: Ideally, the command's output would go to the run log for $_VERBOSE >= 1
|
||||
# but the 'tee' tool doesn't preserve the status code of its input pipe
|
||||
# in case of error.
|
||||
run () {
|
||||
local LOGILE
|
||||
if [ "$_RUN_LOG" ]; then
|
||||
LOGFILE=$_RUN_LOG
|
||||
else
|
||||
LOGFILE=/dev/null
|
||||
fi
|
||||
|
||||
if [ "$_VERBOSE" -ge 1 ]; then
|
||||
echo "COMMAND: $@"
|
||||
"$@"
|
||||
else
|
||||
"$@" >>$LOGFILE 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Same as run(), but only dump command output for $_VERBOSE >= 2
|
||||
run2 () {
|
||||
local LOGILE
|
||||
if [ "$_RUN_LOG" ]; then
|
||||
LOGFILE=$_RUN_LOG
|
||||
else
|
||||
LOGFILE=/dev/null
|
||||
fi
|
||||
|
||||
if [ "$_VERBOSE" -ge 1 ]; then
|
||||
echo "COMMAND: $@"
|
||||
fi
|
||||
if [ "$_VERBOSE" -ge 2 ]; then
|
||||
"$@"
|
||||
else
|
||||
"$@" >>$LOGFILE 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Extract number of cores to speed up the builds
|
||||
# Out: number of CPU cores
|
||||
get_core_count () {
|
||||
case $(uname -s) in
|
||||
Linux)
|
||||
grep -c -e '^processor' /proc/cpuinfo
|
||||
;;
|
||||
Darwin)
|
||||
sysctl -n hw.ncpu
|
||||
;;
|
||||
CYGWIN*|*_NT-*)
|
||||
echo $NUMBER_OF_PROCESSORS
|
||||
;;
|
||||
*)
|
||||
echo 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
# Check for the Android ADB program.
|
||||
#
|
||||
# On success, return nothing, but updates internal variables so later calls to
|
||||
# adb_shell, adb_push, etc.. will work. You can get the path to the ADB program
|
||||
# with adb_get_program if needed.
|
||||
#
|
||||
# On failure, returns 1, and updates the internal adb error message, which can
|
||||
# be retrieved with adb_get_error.
|
||||
#
|
||||
# $1: optional ADB program path.
|
||||
# Return: success or failure.
|
||||
_ADB=
|
||||
_ADB_STATUS=
|
||||
_ADB_ERROR=
|
||||
|
||||
adb_check () {
|
||||
# First, try to find the executable in the path, or the SDK install dir.
|
||||
_ADB=$1
|
||||
if [ -z "$_ADB" ]; then
|
||||
_ADB=$(which adb 2>/dev/null)
|
||||
if [ -z "$_ADB" -a "$ANDROID_SDK_ROOT" ]; then
|
||||
_ADB=$ANDROID_SDK_ROOT/platform-tools/adb
|
||||
if [ ! -f "$_ADB" ]; then
|
||||
_ADB=
|
||||
fi
|
||||
fi
|
||||
if [ -z "$_ADB" ]; then
|
||||
_ADB_STATUS=1
|
||||
_ADB_ERROR="The Android 'adb' tool is not in your path."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Found ADB program: $_ADB"
|
||||
|
||||
# Check that it works correctly
|
||||
local ADB_VERSION
|
||||
ADB_VERSION=$("$_ADB" version 2>/dev/null)
|
||||
case $ADB_VERSION in
|
||||
"Android Debug Bridge "*) # Pass
|
||||
log "Found ADB version: $ADB_VERSION"
|
||||
;;
|
||||
*) # Fail
|
||||
_ADB_ERROR="Your ADB binary reports a bad version ($ADB_VERSION): $_ADB"
|
||||
_ADB_STATUS=1
|
||||
return 1
|
||||
esac
|
||||
|
||||
_ADB_STATUS=0
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# Return the path to the Android ADB program, if correctly detected.
|
||||
# On failure, return the empty string.
|
||||
# Out: ADB program path (or empty on failure)
|
||||
# Return: success or failure.
|
||||
adb_get_program () {
|
||||
# Return cached value as soon as possible.
|
||||
if [ -z "$_ADB_STATUS" ]; then
|
||||
adb_check $1
|
||||
fi
|
||||
echo "$_ADB"
|
||||
return $_ADB_STATUS
|
||||
}
|
||||
|
||||
# Return the error corresponding to the last ADB function failure.
|
||||
adb_get_error () {
|
||||
echo "$_ADB_ERROR"
|
||||
}
|
||||
|
||||
# Check that there is one device connected through ADB.
|
||||
# In case of failure, use adb_get_error to know why this failed.
|
||||
# $1: Optional adb program path
|
||||
# Return: success or failure.
|
||||
_ADB_DEVICE=
|
||||
_ADB_DEVICE_STATUS=
|
||||
adb_check_device () {
|
||||
if [ "$_ADB_DEVICE_STATUS" ]; then
|
||||
return $_ADB_DEVICE_STATUS
|
||||
fi
|
||||
|
||||
# Check for ADB.
|
||||
if ! adb_check $1; then
|
||||
_ADB_DEVICE_STATUS=$_ADB_STATUS
|
||||
return 1
|
||||
fi
|
||||
|
||||
local ADB_DEVICES NUM_DEVICES FINGERPRINT
|
||||
|
||||
# Count the number of connected devices.
|
||||
ADB_DEVICES=$("$_ADB" devices 2>/dev/null | awk '$2 == "device" { print $1; }')
|
||||
NUM_DEVICES=$(echo "$ADB_DEVICES" | wc -l)
|
||||
case $NUM_DEVICES in
|
||||
0)
|
||||
_ADB_ERROR="No Android device connected. Please connect one to your machine."
|
||||
_ADB_DEVICE_STATUS=1
|
||||
return 1
|
||||
;;
|
||||
1) # Pass
|
||||
# Ensure the same device will be called in later adb_shell calls.
|
||||
export ANDROID_SERIAL=$ADB_DEVICES
|
||||
;;
|
||||
*) # 2 or more devices.
|
||||
if [ "$ANDROID_SERIAL" ]; then
|
||||
ADB_DEVICES=$ANDROID_SERIAL
|
||||
NUM_DEVICES=1
|
||||
else
|
||||
_ADB_ERROR="More than one Android device connected. \
|
||||
Please define ANDROID_SERIAL in your environment"
|
||||
_ADB_DEVICE_STATUS=1
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
_ADB_DEVICE_STATUS=0
|
||||
_ADB_DEVICE=$ADB_DEVICES
|
||||
|
||||
FINGERPRINT=$(adb_shell getprop ro.build.fingerprint)
|
||||
log "Using ADB device: $ANDROID_SERIAL ($FINGERPRINT)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# The 'adb shell' command is pretty hopeless, try to make sense of it by:
|
||||
# 1/ Removing trailing \r from line endings.
|
||||
# 2/ Ensuring the function returns the command's status code.
|
||||
#
|
||||
# $1+: Command
|
||||
# Out: command output (stdout + stderr combined)
|
||||
# Return: command exit status
|
||||
adb_shell () {
|
||||
local RET ADB_LOG
|
||||
# Check for ADB device.
|
||||
adb_check_device || return 1
|
||||
ADB_LOG=$(mktemp "${TMPDIR:-/tmp}/adb-XXXXXXXX")
|
||||
"$_ADB" shell "$@" ";" echo \$? > "$ADB_LOG" 2>&1
|
||||
sed -i -e 's![[:cntrl:]]!!g' "$ADB_LOG" # Remove \r.
|
||||
RET=$(sed -e '$!d' "$ADB_LOG") # Last line contains status code.
|
||||
sed -e '$d' "$ADB_LOG" # Print everything except last line.
|
||||
rm -f "$ADB_LOG"
|
||||
return $RET
|
||||
}
|
||||
|
||||
# Push a file to a device.
|
||||
# $1: source file path
|
||||
# $2: device target file path
|
||||
# Return: success or failure.
|
||||
adb_push () {
|
||||
adb_check_device || return 1
|
||||
run "$_ADB" push "$1" "$2"
|
||||
}
|
||||
|
||||
# Pull a file from a device
|
||||
# $1: device file path
|
||||
# $2: target host file path
|
||||
# Return: success or failure.
|
||||
adb_pull () {
|
||||
adb_check_device || return 1
|
||||
run "$_ADB" pull "$1" "$2"
|
||||
}
|
||||
|
||||
# Same as adb_push, but will panic if the operations didn't succeed.
|
||||
adb_install () {
|
||||
adb_push "$@"
|
||||
fail_panic "Failed to install $1 to the Android device at $2"
|
||||
}
|
||||
|
103
android/google_breakpad/Android.mk
Normal file
103
android/google_breakpad/Android.mk
Normal file
@ -0,0 +1,103 @@
|
||||
# Copyright (c) 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# ndk-build module definition for the Google Breakpad client library
|
||||
#
|
||||
# To use this file, do the following:
|
||||
#
|
||||
# 1/ Include this file from your own Android.mk, either directly
|
||||
# or with through the NDK's import-module function.
|
||||
#
|
||||
# 2/ Use the client static library in your project with:
|
||||
#
|
||||
# LOCAL_STATIC_LIBRARIES += breakpad_client
|
||||
#
|
||||
# 3/ In your source code, include "src/client/linux/exception_handler.h"
|
||||
# and use the Linux instructions to use it.
|
||||
#
|
||||
# This module works with either the STLport or GNU libstdc++, but you need
|
||||
# to select one in your Application.mk
|
||||
#
|
||||
|
||||
# The top Google Breakpad directory.
|
||||
# We assume this Android.mk to be under 'android/google_breakpad'
|
||||
|
||||
LOCAL_PATH := $(call my-dir)/../..
|
||||
|
||||
# Defube the client library module, as a simple static library that
|
||||
# exports the right include path / linker flags to its users.
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := breakpad_client
|
||||
|
||||
LOCAL_CPP_EXTENSION := .cc
|
||||
|
||||
# Breakpad uses inline ARM assembly that requires the library
|
||||
# to be built in ARM mode. Otherwise, the build will fail with
|
||||
# cryptic assembler messages like:
|
||||
# Compile++ thumb : google_breakpad_client <= crash_generation_client.cc
|
||||
# /tmp/cc8aMSoD.s: Assembler messages:
|
||||
# /tmp/cc8aMSoD.s:132: Error: invalid immediate: 288 is out of range
|
||||
# /tmp/cc8aMSoD.s:244: Error: invalid immediate: 296 is out of range
|
||||
LOCAL_ARM_MODE := arm
|
||||
|
||||
# List of client source files, directly taken from Makefile.am
|
||||
LOCAL_SRC_FILES := \
|
||||
src/client/linux/crash_generation/crash_generation_client.cc \
|
||||
src/client/linux/dump_writer_common/thread_info.cc \
|
||||
src/client/linux/dump_writer_common/ucontext_reader.cc \
|
||||
src/client/linux/handler/exception_handler.cc \
|
||||
src/client/linux/handler/minidump_descriptor.cc \
|
||||
src/client/linux/log/log.cc \
|
||||
src/client/linux/microdump_writer/microdump_writer.cc \
|
||||
src/client/linux/minidump_writer/linux_dumper.cc \
|
||||
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
|
||||
src/client/linux/minidump_writer/minidump_writer.cc \
|
||||
src/client/minidump_file_writer.cc \
|
||||
src/common/android/breakpad_getcontext.S \
|
||||
src/common/convert_UTF.c \
|
||||
src/common/md5.cc \
|
||||
src/common/string_conversion.cc \
|
||||
src/common/linux/elfutils.cc \
|
||||
src/common/linux/file_id.cc \
|
||||
src/common/linux/guid_creator.cc \
|
||||
src/common/linux/linux_libc_support.cc \
|
||||
src/common/linux/memory_mapped_file.cc \
|
||||
src/common/linux/safe_readlink.cc
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src/common/android/include \
|
||||
$(LOCAL_PATH)/src
|
||||
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_EXPORT_LDLIBS := -llog
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# Done.
|
555
android/run-checks.sh
Executable file
555
android/run-checks.sh
Executable file
@ -0,0 +1,555 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2012 Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Sanitize the environment
|
||||
export LANG=C
|
||||
export LC_ALL=C
|
||||
|
||||
if [ "$BASH_VERSION" ]; then
|
||||
set -o posix
|
||||
fi
|
||||
|
||||
PROGDIR=$(dirname "$0")
|
||||
PROGDIR=$(cd "$PROGDIR" && pwd)
|
||||
PROGNAME=$(basename "$0")
|
||||
|
||||
. $PROGDIR/common-functions.sh
|
||||
|
||||
DEFAULT_ABI="armeabi"
|
||||
VALID_ABIS="armeabi armeabi-v7a x86 mips"
|
||||
|
||||
ABI=
|
||||
ADB=
|
||||
ALL_TESTS=
|
||||
ENABLE_M32=
|
||||
HELP=
|
||||
HELP_ALL=
|
||||
NDK_DIR=
|
||||
NO_CLEANUP=
|
||||
NO_DEVICE=
|
||||
NUM_JOBS=$(get_core_count)
|
||||
TMPDIR=
|
||||
|
||||
for opt do
|
||||
# The following extracts the value if the option is like --name=<value>.
|
||||
optarg=$(expr -- $opt : '^--[^=]*=\(.*\)$')
|
||||
case $opt in
|
||||
--abi=*) ABI=$optarg;;
|
||||
--adb=*) ADB=$optarg;;
|
||||
--all-tests) ALL_TESTS=true;;
|
||||
--enable-m32) ENABLE_M32=true;;
|
||||
--help|-h|-?) HELP=TRUE;;
|
||||
--help-all) HELP_ALL=true;;
|
||||
--jobs=*) NUM_JOBS=$optarg;;
|
||||
--ndk-dir=*) NDK_DIR=$optarg;;
|
||||
--tmp-dir=*) TMPDIR=$optarg;;
|
||||
--no-cleanup) NO_CLEANUP=true;;
|
||||
--no-device) NO_DEVICE=true;;
|
||||
--quiet) decrease_verbosity;;
|
||||
--verbose) increase_verbosity;;
|
||||
-*) panic "Invalid option '$opt', see --help for details.";;
|
||||
*) panic "This script doesn't take any parameters. See --help for details."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$HELP" -o "$HELP_ALL" ]; then
|
||||
echo "\
|
||||
Usage: $PROGNAME [options]
|
||||
|
||||
This script is used to check that your Google Breakpad source tree can
|
||||
be properly built for Android, and that the client library and host tools
|
||||
work properly together.
|
||||
"
|
||||
if [ "$HELP_ALL" ]; then
|
||||
echo "\
|
||||
In more details, this script will:
|
||||
|
||||
- Rebuild the host version of Google Breakpad in a temporary
|
||||
directory (with the Auto-tools based build system).
|
||||
|
||||
- Rebuild the Android client library with the Google Breakpad build
|
||||
system (using autotools/configure). This requires that you define
|
||||
ANDROID_NDK_ROOT in your environment to point to a valid Android NDK
|
||||
installation directory, or use the --ndk-dir=<path> option.
|
||||
|
||||
- Rebuild the Android client library and a test crashing program with the
|
||||
Android NDK build system (ndk-build).
|
||||
|
||||
- Require an Android device connected to your machine, and the 'adb'
|
||||
tool in your path. They are used to:
|
||||
|
||||
- Install and run a test crashing program.
|
||||
- Extract the corresponding minidump from the device.
|
||||
- Dump the symbols from the test program on the host with 'dump_syms'
|
||||
- Generate a stack trace with 'minidump_stackwalk'
|
||||
- Check the stack trace content for valid source file locations.
|
||||
|
||||
You can however skip this requirement and only test the builds by using
|
||||
the --no-device flag.
|
||||
|
||||
By default, all generated files will be created in a temporary directory
|
||||
that is removed when the script completion. If you want to inspect the
|
||||
files, use the --no-cleanup option.
|
||||
|
||||
Finally, use --verbose to increase the verbosity level, this will help
|
||||
you see which exact commands are being issues and their result. Use the
|
||||
flag twice for even more output. Use --quiet to decrease verbosity
|
||||
instead and run the script silently.
|
||||
|
||||
If you have a device connected, the script will probe it to determine
|
||||
its primary CPU ABI, and build the test program for it. You can however
|
||||
use the --abi=<name> option to override this (this can be useful to check
|
||||
the secondary ABI, e.g. using --abi=armeabi to check that such a program
|
||||
works correctly on an ARMv7-A device).
|
||||
|
||||
If you don't have a device connected, the test program will be built (but
|
||||
not run) with the default '$DEFAULT_ABI' ABI. Again, you can use
|
||||
--abi=<name> to override this. Valid ABI names are:
|
||||
|
||||
$VALID_ABIS
|
||||
|
||||
The script will only run the client library unit test on the device
|
||||
by default. You can use --all-tests to also build and run the unit
|
||||
tests for the Breakpad tools and processor, but be warned that this
|
||||
adds several minutes of testing time. --all-tests will also run the
|
||||
host unit tests suite.
|
||||
"
|
||||
|
||||
fi # HELP_ALL
|
||||
|
||||
echo "\
|
||||
Valid options:
|
||||
|
||||
--help|-h|-? Display this message.
|
||||
--help-all Display extended help.
|
||||
--enable-m32 Build 32-bit version of host tools.
|
||||
--abi=<name> Specify target CPU ABI [auto-detected].
|
||||
--jobs=<count> Run <count> build tasks in parallel [$NUM_JOBS].
|
||||
--ndk-dir=<path> Specify NDK installation directory.
|
||||
--tmp-dir=<path> Specify temporary directory (will be wiped-out).
|
||||
--adb=<path> Specify adb program path.
|
||||
--no-cleanup Don't remove temporary directory after completion.
|
||||
--no-device Do not try to detect devices, nor run crash test.
|
||||
--all-tests Run all unit tests (i.e. tools and processor ones too).
|
||||
--verbose Increase verbosity.
|
||||
--quiet Decrease verbosity."
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TESTAPP_DIR=$PROGDIR/sample_app
|
||||
|
||||
# Select NDK install directory.
|
||||
if [ -z "$NDK_DIR" ]; then
|
||||
if [ -z "$ANDROID_NDK_ROOT" ]; then
|
||||
panic "Please define ANDROID_NDK_ROOT in your environment, or use \
|
||||
--ndk-dir=<path>."
|
||||
fi
|
||||
NDK_DIR="$ANDROID_NDK_ROOT"
|
||||
log "Found NDK directory: $NDK_DIR"
|
||||
else
|
||||
log "Using NDK directory: $NDK_DIR"
|
||||
fi
|
||||
# Small sanity check.
|
||||
NDK_BUILD="$NDK_DIR/ndk-build"
|
||||
if [ ! -f "$NDK_BUILD" ]; then
|
||||
panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR"
|
||||
fi
|
||||
|
||||
# Ensure the temporary directory is deleted on exit, except if the --no-cleanup
|
||||
# option is used.
|
||||
|
||||
clean_tmpdir () {
|
||||
if [ "$TMPDIR" ]; then
|
||||
if [ -z "$NO_CLEANUP" ]; then
|
||||
log "Cleaning up: $TMPDIR"
|
||||
rm -rf "$TMPDIR"
|
||||
else
|
||||
dump "Temporary directory contents preserved: $TMPDIR"
|
||||
fi
|
||||
fi
|
||||
exit "$@"
|
||||
}
|
||||
|
||||
atexit clean_tmpdir
|
||||
|
||||
# If --tmp-dir=<path> is not used, create a temporary directory.
|
||||
# Otherwise, start by cleaning up the user-provided path.
|
||||
if [ -z "$TMPDIR" ]; then
|
||||
TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX)
|
||||
fail_panic "Can't create temporary directory!"
|
||||
log "Using temporary directory: $TMPDIR"
|
||||
else
|
||||
if [ ! -d "$TMPDIR" ]; then
|
||||
mkdir -p "$TMPDIR"
|
||||
fail_panic "Can't create temporary directory: $TMPDIR"
|
||||
else
|
||||
log "Cleaning up temporary directory: $TMPDIR"
|
||||
rm -rf "$TMPDIR"/*
|
||||
fail_panic "Cannot cleanup temporary directory!"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$NO_DEVICE" ]; then
|
||||
if ! adb_check_device $ADB; then
|
||||
echo "$(adb_get_error)"
|
||||
echo "Use --no-device to build the code without running any tests."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
BUILD_LOG="$TMPDIR/build.log"
|
||||
RUN_LOG="$TMPDIR/run.log"
|
||||
CRASH_LOG="$TMPDIR/crash.log"
|
||||
|
||||
set_run_log "$RUN_LOG"
|
||||
|
||||
TMPHOST="$TMPDIR/host-local"
|
||||
|
||||
cd "$TMPDIR"
|
||||
|
||||
# Build host version of the tools
|
||||
dump "Building host binaries."
|
||||
CONFIGURE_FLAGS=
|
||||
if [ "$ENABLE_M32" ]; then
|
||||
CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32"
|
||||
fi
|
||||
(
|
||||
run mkdir "$TMPDIR/build-host" &&
|
||||
run cd "$TMPDIR/build-host" &&
|
||||
run2 "$PROGDIR/../configure" --prefix="$TMPHOST" $CONFIGURE_FLAGS &&
|
||||
run2 make -j$NUM_JOBS install
|
||||
)
|
||||
fail_panic "Can't build host binaries!"
|
||||
|
||||
if [ "$ALL_TESTS" ]; then
|
||||
dump "Running host unit tests."
|
||||
(
|
||||
run cd "$TMPDIR/build-host" &&
|
||||
run2 make -j$NUM_JOBS check
|
||||
)
|
||||
fail_panic "Host unit tests failed!!"
|
||||
fi
|
||||
|
||||
TMPBIN=$TMPHOST/bin
|
||||
|
||||
# Generate a stand-alone NDK toolchain
|
||||
|
||||
# Extract CPU ABI and architecture from device, if any.
|
||||
if adb_check_device; then
|
||||
DEVICE_ABI=$(adb_shell getprop ro.product.cpu.abi)
|
||||
DEVICE_ABI2=$(adb_shell getprop ro.product.cpu.abi2)
|
||||
if [ -z "$DEVICE_ABI" ]; then
|
||||
panic "Can't extract ABI from connected device!"
|
||||
fi
|
||||
if [ "$DEVICE_ABI2" ]; then
|
||||
dump "Found device ABIs: $DEVICE_ABI $DEVICE_ABI2"
|
||||
else
|
||||
dump "Found device ABI: $DEVICE_ABI"
|
||||
DEVICE_ABI2=$DEVICE_ABI
|
||||
fi
|
||||
|
||||
# If --abi=<name> is used, check that the device supports it.
|
||||
if [ "$ABI" -a "$DEVICE_ABI" != "$ABI" -a "$DEVICE_ABI2" != "$ABI" ]; then
|
||||
dump "ERROR: Device ABI(s) do not match --abi command-line value ($ABI)!"
|
||||
panic "Please use --no-device to skip device tests."
|
||||
fi
|
||||
|
||||
if [ -z "$ABI" ]; then
|
||||
ABI=$DEVICE_ABI
|
||||
dump "Using CPU ABI: $ABI (device)"
|
||||
else
|
||||
dump "Using CPU ABI: $ABI (command-line)"
|
||||
fi
|
||||
else
|
||||
if [ -z "$ABI" ]; then
|
||||
# No device connected, choose default ABI
|
||||
ABI=$DEFAULT_ABI
|
||||
dump "Using CPU ABI: $ABI (default)"
|
||||
else
|
||||
dump "Using CPU ABI: $ABI (command-line)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check the ABI value
|
||||
VALID=
|
||||
for VALID_ABI in $VALID_ABIS; do
|
||||
if [ "$ABI" = "$VALID_ABI" ]; then
|
||||
VALID=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$VALID" ]; then
|
||||
panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS"
|
||||
fi
|
||||
|
||||
# Extract architecture name from ABI
|
||||
case $ABI in
|
||||
armeabi*) ARCH=arm;;
|
||||
*) ARCH=$ABI;;
|
||||
esac
|
||||
|
||||
# Extract GNU configuration name
|
||||
case $ARCH in
|
||||
arm)
|
||||
GNU_CONFIG=arm-linux-androideabi
|
||||
;;
|
||||
x86)
|
||||
GNU_CONFIG=i686-linux-android
|
||||
;;
|
||||
mips)
|
||||
GNU_CONFIG=mipsel-linux-android
|
||||
;;
|
||||
*)
|
||||
GNU_CONFIG="$ARCH-linux-android"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Generate standalone NDK toolchain installation
|
||||
NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain"
|
||||
echo "Generating NDK standalone toolchain installation"
|
||||
mkdir -p "$NDK_STANDALONE"
|
||||
# NOTE: The --platform=android-9 is required to provide <regex.h> for GTest.
|
||||
run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \
|
||||
--arch="$ARCH" \
|
||||
--platform=android-9 \
|
||||
--install-dir="$NDK_STANDALONE"
|
||||
fail_panic "Can't generate standalone NDK toolchain installation!"
|
||||
|
||||
# Rebuild the client library, processor and tools with the auto-tools based
|
||||
# build system. Even though it's not going to be used, this checks that this
|
||||
# still works correctly.
|
||||
echo "Building full Android binaries with configure/make"
|
||||
TMPTARGET="$TMPDIR/target-local"
|
||||
(
|
||||
PATH="$NDK_STANDALONE/bin:$PATH"
|
||||
run mkdir "$TMPTARGET" &&
|
||||
run mkdir "$TMPDIR"/build-target &&
|
||||
run cd "$TMPDIR"/build-target &&
|
||||
run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
|
||||
--host="$GNU_CONFIG" &&
|
||||
run2 make -j$NUM_JOBS install
|
||||
)
|
||||
fail_panic "Could not rebuild Android binaries!"
|
||||
|
||||
# Build and/or run unit test suite.
|
||||
# If --no-device is used, only rebuild it, otherwise, run in on the
|
||||
# connected device.
|
||||
if [ "$NO_DEVICE" ]; then
|
||||
ACTION="Building"
|
||||
# This is a trick to force the Makefile to ignore running the scripts.
|
||||
TESTS_ENVIRONMENT="TESTS_ENVIRONMENT=true"
|
||||
else
|
||||
ACTION="Running"
|
||||
TESTS_ENVIRONMENT=
|
||||
fi
|
||||
|
||||
(
|
||||
PATH="$NDK_STANDALONE/bin:$PATH"
|
||||
run cd "$TMPDIR"/build-target &&
|
||||
# Reconfigure to only run the client unit test suite.
|
||||
# This one should _never_ fail.
|
||||
dump "$ACTION Android client library unit tests."
|
||||
run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
|
||||
--host="$GNU_CONFIG" \
|
||||
--disable-tools \
|
||||
--disable-processor &&
|
||||
run make -j$NUM_JOBS check $TESTS_ENVIRONMENT || exit $?
|
||||
|
||||
if [ "$ALL_TESTS" ]; then
|
||||
dump "$ACTION Tools and processor unit tests."
|
||||
# Reconfigure to run the processor and tools tests.
|
||||
# Most of these fail for now, so do not worry about it.
|
||||
run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
|
||||
--host="$GNU_CONFIG" &&
|
||||
run make -j$NUM_JOBS check $TESTS_ENVIRONMENT
|
||||
if [ $? != 0 ]; then
|
||||
dump "Tools and processor unit tests failed as expected. \
|
||||
Use --verbose for results."
|
||||
fi
|
||||
fi
|
||||
)
|
||||
fail_panic "Client library unit test suite failed!"
|
||||
|
||||
# Copy sources to temporary directory
|
||||
PROJECT_DIR=$TMPDIR/project
|
||||
dump "Copying test program sources to: $PROJECT_DIR"
|
||||
run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" &&
|
||||
run rm -rf "$PROJECT_DIR/obj" &&
|
||||
run rm -rf "$PROJECT_DIR/libs"
|
||||
fail_panic "Could not copy test program sources to: $PROJECT_DIR"
|
||||
|
||||
# Build the test program with ndk-build.
|
||||
dump "Building test program with ndk-build"
|
||||
export NDK_MODULE_PATH="$PROGDIR"
|
||||
NDK_BUILD_FLAGS="-j$NUM_JOBS"
|
||||
if verbosity_is_higher_than 1; then
|
||||
NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1"
|
||||
fi
|
||||
run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI
|
||||
fail_panic "Can't build test program!"
|
||||
|
||||
# Unless --no-device was used, stop right here if ADB isn't in the path,
|
||||
# or there is no connected device.
|
||||
if [ "$NO_DEVICE" ]; then
|
||||
dump "Done. Please connect a device to run all tests!"
|
||||
clean_exit 0
|
||||
fi
|
||||
|
||||
# Push the program to the device.
|
||||
TESTAPP=test_google_breakpad
|
||||
TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad"
|
||||
if [ ! -f "$TESTAPP_FILE" ]; then
|
||||
panic "Device requires '$ABI' binaries. None found!"
|
||||
fi
|
||||
|
||||
# Run the program there
|
||||
dump "Installing test program on device"
|
||||
DEVICE_TMP=/data/local/tmp
|
||||
adb_push "$TESTAPP_FILE" "$DEVICE_TMP/"
|
||||
fail_panic "Cannot push test program to device!"
|
||||
|
||||
dump "Running test program on device"
|
||||
adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null
|
||||
if [ $? = 0 ]; then
|
||||
panic "Test program did *not* crash as expected!"
|
||||
fi
|
||||
if verbosity_is_higher_than 0; then
|
||||
echo -n "Crash log: "
|
||||
cat "$CRASH_LOG"
|
||||
fi
|
||||
|
||||
# Extract minidump from device
|
||||
MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG")
|
||||
MINIDUMP_NAME=$(basename "$MINIDUMP_NAME")
|
||||
if [ -z "$MINIDUMP_NAME" ]; then
|
||||
panic "Test program didn't write minidump properly!"
|
||||
fi
|
||||
|
||||
dump "Extracting minidump: $MINIDUMP_NAME"
|
||||
adb_pull "$DEVICE_TMP/$MINIDUMP_NAME" .
|
||||
fail_panic "Can't extract minidump!"
|
||||
|
||||
dump "Parsing test program symbols"
|
||||
if verbosity_is_higher_than 1; then
|
||||
log "COMMAND: $TMPBIN/dump_syms \
|
||||
$PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym"
|
||||
fi
|
||||
"$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym
|
||||
fail_panic "dump_syms doesn't work!"
|
||||
|
||||
VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym)
|
||||
dump "Found module version: $VERSION"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "ERROR: Can't find proper module version from symbol dump!"
|
||||
head -n5 $TESTAPP.sym
|
||||
clean_exit 1
|
||||
fi
|
||||
|
||||
run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION"
|
||||
run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/"
|
||||
|
||||
dump "Generating stack trace"
|
||||
# Don't use 'run' to be able to send stdout and stderr to two different files.
|
||||
log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols"
|
||||
"$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \
|
||||
"$TMPDIR/symbols" \
|
||||
> "$BUILD_LOG" 2>>"$RUN_LOG"
|
||||
fail_panic "minidump_stackwalk doesn't work!"
|
||||
|
||||
dump "Checking stack trace content"
|
||||
|
||||
if verbosity_is_higher_than 1; then
|
||||
cat "$BUILD_LOG"
|
||||
fi
|
||||
|
||||
# The generated stack trace should look like the following:
|
||||
#
|
||||
# Thread 0 (crashed)
|
||||
# 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4]
|
||||
# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
|
||||
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
|
||||
# sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84
|
||||
# Found by: given as instruction pointer in context
|
||||
# 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3]
|
||||
# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
|
||||
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
|
||||
# sp = 0xbea2cb50 pc = 0x00009025
|
||||
# Found by: call frame info
|
||||
# 2 libc.so + 0x164e5
|
||||
# r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c
|
||||
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
|
||||
# sp = 0xbea2cc18 pc = 0x400c34e7
|
||||
# Found by: call frame info
|
||||
# ...
|
||||
#
|
||||
# The most important part for us is ensuring that the source location could
|
||||
# be extracted, so look at the 'test_breakpad.cpp' references here.
|
||||
#
|
||||
# First, extract all the lines with test_google_breakpad! in them, and
|
||||
# dump the corresponding crash location.
|
||||
#
|
||||
# Note that if the source location can't be extracted, the second field
|
||||
# will only be 'test_google_breakpad' without the exclamation mark.
|
||||
#
|
||||
LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG")
|
||||
|
||||
if [ -z "$LOCATIONS" ]; then
|
||||
if verbosity_is_lower_than 1; then
|
||||
cat "$BUILD_LOG"
|
||||
fi
|
||||
panic "No source location found in stack trace!"
|
||||
fi
|
||||
|
||||
# Now check that they all match "[<source file>"
|
||||
BAD_LOCATIONS=
|
||||
for LOCATION in $LOCATIONS; do
|
||||
case $LOCATION in
|
||||
# Escape the opening bracket, or some shells like Dash will not
|
||||
# match them properly.
|
||||
\[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable
|
||||
;;
|
||||
*) # Everything else is not!
|
||||
BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$BAD_LOCATIONS" ]; then
|
||||
dump "ERROR: Generated stack trace doesn't contain valid source locations:"
|
||||
cat "$BUILD_LOG"
|
||||
echo "Bad locations are: $BAD_LOCATIONS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All clear! Congratulations."
|
||||
|
32
android/sample_app/README
Normal file
32
android/sample_app/README
Normal file
@ -0,0 +1,32 @@
|
||||
This is a sample Android executable that can be used to test the
|
||||
Google Breakpad client library on Android.
|
||||
|
||||
Its purpose is simply to crash and generate a minidump under /data/local/tmp.
|
||||
|
||||
Build instructions:
|
||||
|
||||
cd android/sample_app
|
||||
$NDK/ndk-build
|
||||
|
||||
Where $NDK points to a valid Android NDK installation.
|
||||
|
||||
Usage instructions:
|
||||
|
||||
After buildind the test program, send it to a device, then run it as
|
||||
the shell UID:
|
||||
|
||||
adb push libs/armeabi/test_google_breakpad /data/local/tmp
|
||||
adb shell /data/local/tmp/test_google_breakpad
|
||||
|
||||
This will simply crash after dumping the name of the generated minidump
|
||||
file.
|
||||
|
||||
See jni/test_breakpad.cpp for details.
|
||||
|
||||
Use 'armeabi-v7a' instead of 'armeabi' above to test the ARMv7-A version
|
||||
of the binary.
|
||||
|
||||
Note:
|
||||
If you plan to use the library in a regular Android application, store
|
||||
the minidump files either to your app-specific directory, or to the SDCard
|
||||
(the latter requiring a specific permission).
|
44
android/sample_app/jni/Android.mk
Normal file
44
android/sample_app/jni/Android.mk
Normal file
@ -0,0 +1,44 @@
|
||||
# Copyright (c) 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := test_google_breakpad
|
||||
LOCAL_SRC_FILES := test_breakpad.cpp
|
||||
LOCAL_STATIC_LIBRARIES += breakpad_client
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# If NDK_MODULE_PATH is defined, import the module, otherwise do a direct
|
||||
# includes. This allows us to build in all scenarios easily.
|
||||
ifneq ($(NDK_MODULE_PATH),)
|
||||
$(call import-module,google_breakpad)
|
||||
else
|
||||
include $(LOCAL_PATH)/../../google_breakpad/Android.mk
|
||||
endif
|
32
android/sample_app/jni/Application.mk
Normal file
32
android/sample_app/jni/Application.mk
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
APP_STL := stlport_static
|
||||
APP_ABI := all
|
||||
APP_CXXFLAGS := -std=c++11 -D__STDC_LIMIT_MACROS
|
57
android/sample_app/jni/test_breakpad.cpp
Normal file
57
android/sample_app/jni/test_breakpad.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
|
||||
namespace {
|
||||
|
||||
bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
|
||||
void* context,
|
||||
bool succeeded) {
|
||||
printf("Dump path: %s\n", descriptor.path());
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void Crash() {
|
||||
volatile int* a = reinterpret_cast<volatile int*>(NULL);
|
||||
*a = 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
google_breakpad::MinidumpDescriptor descriptor(".");
|
||||
google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback,
|
||||
NULL, true, -1);
|
||||
Crash();
|
||||
return 0;
|
||||
}
|
131
android/test-driver
Executable file
131
android/test-driver
Executable file
@ -0,0 +1,131 @@
|
||||
#! /bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
# Slightly modified for Android, see ANDROID comment below.
|
||||
|
||||
scriptversion=2012-06-27.10; # UTC
|
||||
|
||||
# Copyright (C) 2011-2013 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$0: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--] TEST-SCRIPT
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
# TODO: better error handling in option parsing (in particular, ensure
|
||||
# TODO: $log_file, $trs_file and $test_name are defined).
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=no
|
||||
color_tests=no
|
||||
enable_hard_errors=yes
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $color_tests = yes; then
|
||||
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||
red='[0;31m' # Red.
|
||||
grn='[0;32m' # Green.
|
||||
lgn='[1;32m' # Light green.
|
||||
blu='[1;34m' # Blue.
|
||||
mgn='[0;35m' # Magenta.
|
||||
std='[m' # No color.
|
||||
else
|
||||
red= grn= lgn= blu= mgn= std=
|
||||
fi
|
||||
|
||||
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||
trap "st=129; $do_exit" 1
|
||||
trap "st=130; $do_exit" 2
|
||||
trap "st=141; $do_exit" 13
|
||||
trap "st=143; $do_exit" 15
|
||||
|
||||
# Test script is run here.
|
||||
# ANDROID: old line was: "$@" > $log_file 2>&1
|
||||
progdir=$(dirname "$0")
|
||||
"$progdir/test-shell.sh" "$@" > $log_file 2>&1
|
||||
estatus=$?
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
estatus=1
|
||||
fi
|
||||
|
||||
case $estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
# Register the test result, and other relevant metadata.
|
||||
echo ":test-result: $res" > $trs_file
|
||||
echo ":global-test-result: $res" >> $trs_file
|
||||
echo ":recheck: $recheck" >> $trs_file
|
||||
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
131
android/test-shell.sh
Executable file
131
android/test-shell.sh
Executable file
@ -0,0 +1,131 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2012 Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# A special shell wrapper that can be used to run the Google Breakpad unit
|
||||
# tests on a connected Android device.
|
||||
#
|
||||
# This is designed to be called from the Makefile during 'make check'
|
||||
#
|
||||
|
||||
PROGDIR=$(dirname "$0")
|
||||
PROGNAME=$(basename "$0")
|
||||
. $PROGDIR/common-functions.sh
|
||||
|
||||
# Extract test program name first.
|
||||
TEST_PROGRAM=$1
|
||||
shift
|
||||
|
||||
if [ -z "$TEST_PROGRAM" ]; then
|
||||
panic "No test program/script name on the command-line!"
|
||||
fi
|
||||
|
||||
if [ ! -f "$TEST_PROGRAM" ]; then
|
||||
panic "Can't find test program/script: $TEST_PROGRAM"
|
||||
fi
|
||||
|
||||
# Create test directory on the device
|
||||
TEST_DIR=/data/local/tmp/test-google-breakpad-$$
|
||||
adb_shell mkdir "$TEST_DIR" ||
|
||||
panic "Can't create test directory on device: $TEST_DIR"
|
||||
|
||||
# Ensure that it is always removed when the script exits.
|
||||
clean_test_dir () {
|
||||
# Don't care about success/failure, use '$ADB shell' directly.
|
||||
adb_shell rm -r "$TEST_DIR"
|
||||
}
|
||||
|
||||
atexit clean_test_dir
|
||||
|
||||
TEST_PROGRAM_NAME=$(basename "$TEST_PROGRAM")
|
||||
TEST_PROGRAM_DIR=$(dirname "$TEST_PROGRAM")
|
||||
|
||||
# Handle special case(s) here.
|
||||
DATA_FILES=
|
||||
case $TEST_PROGRAM_NAME in
|
||||
linux_client_unittest)
|
||||
# linux_client_unittest will call another executable at runtime, ensure
|
||||
# it is installed too.
|
||||
adb_install "$TEST_PROGRAM_DIR/linux_dumper_unittest_helper" "$TEST_DIR"
|
||||
# linux_client_unittest loads a shared library at runtime, ensure it is
|
||||
# installed too.
|
||||
adb_install "$TEST_PROGRAM_DIR/linux_client_unittest_shlib" "$TEST_DIR"
|
||||
;;
|
||||
basic_source_line_resolver_unittest)
|
||||
DATA_FILES="module1.out \
|
||||
module2.out \
|
||||
module3_bad.out \
|
||||
module4_bad.out"
|
||||
;;
|
||||
exploitability_unittest)
|
||||
DATA_FILES="scii_read_av.dmp \
|
||||
ascii_read_av_block_write.dmp \
|
||||
ascii_read_av_clobber_write.dmp \
|
||||
ascii_read_av_conditional.dmp \
|
||||
ascii_read_av_non_null.dmp \
|
||||
ascii_read_av_then_jmp.dmp \
|
||||
ascii_read_av_xchg_write.dmp \
|
||||
ascii_write_av.dmp \
|
||||
ascii_write_av_arg_to_call.dmp \
|
||||
exec_av_on_stack.dmp \
|
||||
null_read_av.dmp \
|
||||
null_write_av.dmp \
|
||||
read_av.dmp \
|
||||
null_read_av.dmp \
|
||||
write_av_non_null.dmp"
|
||||
;;
|
||||
fast_source_line_resolver_unittest)
|
||||
DATA_FILES="module0.out \
|
||||
module1.out \
|
||||
module2.out \
|
||||
module3_bad.out \
|
||||
module4_bad.out"
|
||||
;;
|
||||
minidump_processor_unittest|minidump_unittest)
|
||||
DATA_FILES="src/processor/testdata/minidump2.dmp"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Install the data files, their path is relative to the environment
|
||||
# variable 'srcdir'
|
||||
for FILE in $DATA_FILES; do
|
||||
FILEDIR=src/processor/testdata/$(dirname "$FILE")
|
||||
adb_shell mkdir -p "$TEST_DIR/$FILEDIR"
|
||||
adb_install "${srcdir:-.}/$FILE" "$TEST_DIR"/"$FILE"
|
||||
done
|
||||
|
||||
# Copy test program to device
|
||||
adb_install "$TEST_PROGRAM" "$TEST_DIR"
|
||||
|
||||
# Run it
|
||||
adb_shell "cd $TEST_DIR && LD_LIBRARY_PATH=. ./$TEST_PROGRAM_NAME $@"
|
||||
|
||||
# Note: exiting here will call cleanup_exit which will remove the temporary
|
||||
# files from the device.
|
270
autotools/ar-lib
Executable file
270
autotools/ar-lib
Executable file
@ -0,0 +1,270 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for Microsoft lib.exe
|
||||
|
||||
me=ar-lib
|
||||
scriptversion=2012-03-01.08; # UTC
|
||||
|
||||
# Copyright (C) 2010-2014 Free Software Foundation, Inc.
|
||||
# Written by Peter Rosin <peda@lysator.liu.se>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
|
||||
# func_error message
|
||||
func_error ()
|
||||
{
|
||||
echo "$me: $1" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv in
|
||||
mingw)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_at_file at_file operation archive
|
||||
# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE
|
||||
# for each of them.
|
||||
# When interpreting the content of the @FILE, do NOT use func_file_conv,
|
||||
# since the user would need to supply preconverted file names to
|
||||
# binutils ar, at least for MinGW.
|
||||
func_at_file ()
|
||||
{
|
||||
operation=$2
|
||||
archive=$3
|
||||
at_file_contents=`cat "$1"`
|
||||
eval set x "$at_file_contents"
|
||||
shift
|
||||
|
||||
for member
|
||||
do
|
||||
$AR -NOLOGO $operation:"$member" "$archive" || exit $?
|
||||
done
|
||||
}
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
func_error "no command. Try '$0 --help' for more information."
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<EOF
|
||||
Usage: $me [--help] [--version] PROGRAM ACTION ARCHIVE [MEMBER...]
|
||||
|
||||
Members may be specified in a file named with @FILE.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "$me, version $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
if test $# -lt 3; then
|
||||
func_error "you must specify a program, an action and an archive"
|
||||
fi
|
||||
|
||||
AR=$1
|
||||
shift
|
||||
while :
|
||||
do
|
||||
if test $# -lt 2; then
|
||||
func_error "you must specify a program, an action and an archive"
|
||||
fi
|
||||
case $1 in
|
||||
-lib | -LIB \
|
||||
| -ltcg | -LTCG \
|
||||
| -machine* | -MACHINE* \
|
||||
| -subsystem* | -SUBSYSTEM* \
|
||||
| -verbose | -VERBOSE \
|
||||
| -wx* | -WX* )
|
||||
AR="$AR $1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
action=$1
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
orig_archive=$1
|
||||
shift
|
||||
func_file_conv "$orig_archive"
|
||||
archive=$file
|
||||
|
||||
# strip leading dash in $action
|
||||
action=${action#-}
|
||||
|
||||
delete=
|
||||
extract=
|
||||
list=
|
||||
quick=
|
||||
replace=
|
||||
index=
|
||||
create=
|
||||
|
||||
while test -n "$action"
|
||||
do
|
||||
case $action in
|
||||
d*) delete=yes ;;
|
||||
x*) extract=yes ;;
|
||||
t*) list=yes ;;
|
||||
q*) quick=yes ;;
|
||||
r*) replace=yes ;;
|
||||
s*) index=yes ;;
|
||||
S*) ;; # the index is always updated implicitly
|
||||
c*) create=yes ;;
|
||||
u*) ;; # TODO: don't ignore the update modifier
|
||||
v*) ;; # TODO: don't ignore the verbose modifier
|
||||
*)
|
||||
func_error "unknown action specified"
|
||||
;;
|
||||
esac
|
||||
action=${action#?}
|
||||
done
|
||||
|
||||
case $delete$extract$list$quick$replace,$index in
|
||||
yes,* | ,yes)
|
||||
;;
|
||||
yesyes*)
|
||||
func_error "more than one action specified"
|
||||
;;
|
||||
*)
|
||||
func_error "no action specified"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$delete"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_at_file "${1#@}" -REMOVE "$archive"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
$AR -NOLOGO -REMOVE:"$file" "$archive" || exit $?
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
elif test -n "$extract"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
if test $# -gt 0; then
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_at_file "${1#@}" -EXTRACT "$archive"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
$AR -NOLOGO -EXTRACT:"$file" "$archive" || exit $?
|
||||
;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
$AR -NOLOGO -LIST "$archive" | sed -e 's/\\/\\\\/g' | while read member
|
||||
do
|
||||
$AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
|
||||
done
|
||||
fi
|
||||
|
||||
elif test -n "$quick$replace"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
if test -z "$create"; then
|
||||
echo "$me: creating $orig_archive"
|
||||
fi
|
||||
orig_archive=
|
||||
else
|
||||
orig_archive=$archive
|
||||
fi
|
||||
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_file_conv "${1#@}"
|
||||
set x "$@" "@$file"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
set x "$@" "$file"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
shift
|
||||
done
|
||||
|
||||
if test -n "$orig_archive"; then
|
||||
$AR -NOLOGO -OUT:"$archive" "$orig_archive" "$@" || exit $?
|
||||
else
|
||||
$AR -NOLOGO -OUT:"$archive" "$@" || exit $?
|
||||
fi
|
||||
|
||||
elif test -n "$list"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
$AR -NOLOGO -LIST "$archive" || exit $?
|
||||
fi
|
347
autotools/compile
Executable file
347
autotools/compile
Executable file
@ -0,0 +1,347 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-10-14.11; # UTC
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/lib$lib.a"; then
|
||||
found=yes
|
||||
lib=$dir/lib$lib.a
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
849
autotools/config.guess
vendored
849
autotools/config.guess
vendored
File diff suppressed because it is too large
Load Diff
415
autotools/config.sub
vendored
415
autotools/config.sub
vendored
@ -1,44 +1,40 @@
|
||||
#! /bin/sh
|
||||
# Configuration validation subroutine script.
|
||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
|
||||
# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
|
||||
# Inc.
|
||||
# Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2006-09-20'
|
||||
timestamp='2016-01-01'
|
||||
|
||||
# This file is (in principle) common to ALL GNU software.
|
||||
# The presence of a machine in this file suggests that SOME GNU software
|
||||
# can handle that machine. It does not imply ALL GNU software can.
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
# the same distribution terms that you use for the rest of that
|
||||
# program. This Exception is an additional permission under section 7
|
||||
# of the GNU General Public License, version 3 ("GPLv3").
|
||||
|
||||
|
||||
# Please send patches to <config-patches@gnu.org>. Submit a context
|
||||
# diff and a properly formatted ChangeLog entry.
|
||||
# Please send patches to <config-patches@gnu.org>.
|
||||
#
|
||||
# Configuration subroutine to validate and canonicalize a configuration type.
|
||||
# Supply the specified configuration type as an argument.
|
||||
# If it is invalid, we print an error message on stderr and exit with code 1.
|
||||
# Otherwise, we print the canonical config type on stdout and succeed.
|
||||
|
||||
# You can get the latest version of this script from:
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
|
||||
|
||||
# This file is supposed to be the same for all GNU packages
|
||||
# and recognize all the CPU types, system types and aliases
|
||||
# that are meaningful with *any* GNU software.
|
||||
@ -57,8 +53,7 @@ timestamp='2006-09-20'
|
||||
me=`echo "$0" | sed -e 's,.*/,,'`
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION] CPU-MFR-OPSYS
|
||||
$0 [OPTION] ALIAS
|
||||
Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
|
||||
|
||||
Canonicalize a configuration name.
|
||||
|
||||
@ -72,8 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
||||
version="\
|
||||
GNU config.sub ($timestamp)
|
||||
|
||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
Free Software Foundation, Inc.
|
||||
Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
@ -120,12 +114,18 @@ esac
|
||||
# Here we must recognize all the valid KERNEL-OS combinations.
|
||||
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
|
||||
case $maybe_os in
|
||||
nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
|
||||
uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
|
||||
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
|
||||
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
|
||||
knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
|
||||
kopensolaris*-gnu* | \
|
||||
storm-chaos* | os2-emx* | rtmk-nova*)
|
||||
os=-$maybe_os
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
|
||||
;;
|
||||
android-linux)
|
||||
os=-linux-android
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
|
||||
;;
|
||||
*)
|
||||
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
|
||||
if [ $basic_machine != $1 ]
|
||||
@ -148,10 +148,13 @@ case $os in
|
||||
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
|
||||
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
|
||||
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
|
||||
-apple | -axis | -knuth | -cray)
|
||||
-apple | -axis | -knuth | -cray | -microblaze*)
|
||||
os=
|
||||
basic_machine=$1
|
||||
;;
|
||||
-bluegene*)
|
||||
os=-cnk
|
||||
;;
|
||||
-sim | -cisco | -oki | -wec | -winbond)
|
||||
os=
|
||||
basic_machine=$1
|
||||
@ -166,10 +169,10 @@ case $os in
|
||||
os=-chorusos
|
||||
basic_machine=$1
|
||||
;;
|
||||
-chorusrdb)
|
||||
os=-chorusrdb
|
||||
-chorusrdb)
|
||||
os=-chorusrdb
|
||||
basic_machine=$1
|
||||
;;
|
||||
;;
|
||||
-hiux*)
|
||||
os=-hiuxwe2
|
||||
;;
|
||||
@ -214,6 +217,12 @@ case $os in
|
||||
-isc*)
|
||||
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
|
||||
;;
|
||||
-lynx*178)
|
||||
os=-lynxos178
|
||||
;;
|
||||
-lynx*5)
|
||||
os=-lynxos5
|
||||
;;
|
||||
-lynx*)
|
||||
os=-lynxos
|
||||
;;
|
||||
@ -238,59 +247,91 @@ case $basic_machine in
|
||||
# Some are omitted here because they have special meanings below.
|
||||
1750a | 580 \
|
||||
| a29k \
|
||||
| aarch64 | aarch64_be \
|
||||
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
|
||||
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
|
||||
| am33_2.0 \
|
||||
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
|
||||
| arc | arceb \
|
||||
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
|
||||
| avr | avr32 \
|
||||
| ba \
|
||||
| be32 | be64 \
|
||||
| bfin \
|
||||
| c4x | clipper \
|
||||
| c4x | c8051 | clipper \
|
||||
| d10v | d30v | dlx | dsp16xx \
|
||||
| fr30 | frv \
|
||||
| e2k | epiphany \
|
||||
| fido | fr30 | frv | ft32 \
|
||||
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
|
||||
| hexagon \
|
||||
| i370 | i860 | i960 | ia64 \
|
||||
| ip2k | iq2000 \
|
||||
| k1om \
|
||||
| le32 | le64 \
|
||||
| lm32 \
|
||||
| m32c | m32r | m32rle | m68000 | m68k | m88k \
|
||||
| maxq | mb | microblaze | mcore \
|
||||
| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
|
||||
| mips | mipsbe | mipseb | mipsel | mipsle \
|
||||
| mips16 \
|
||||
| mips64 | mips64el \
|
||||
| mips64vr | mips64vrel \
|
||||
| mips64octeon | mips64octeonel \
|
||||
| mips64orion | mips64orionel \
|
||||
| mips64r5900 | mips64r5900el \
|
||||
| mips64vr | mips64vrel \
|
||||
| mips64vr4100 | mips64vr4100el \
|
||||
| mips64vr4300 | mips64vr4300el \
|
||||
| mips64vr5000 | mips64vr5000el \
|
||||
| mips64vr5900 | mips64vr5900el \
|
||||
| mipsisa32 | mipsisa32el \
|
||||
| mipsisa32r2 | mipsisa32r2el \
|
||||
| mipsisa32r6 | mipsisa32r6el \
|
||||
| mipsisa64 | mipsisa64el \
|
||||
| mipsisa64r2 | mipsisa64r2el \
|
||||
| mipsisa64r6 | mipsisa64r6el \
|
||||
| mipsisa64sb1 | mipsisa64sb1el \
|
||||
| mipsisa64sr71k | mipsisa64sr71kel \
|
||||
| mipsr5900 | mipsr5900el \
|
||||
| mipstx39 | mipstx39el \
|
||||
| mn10200 | mn10300 \
|
||||
| moxie \
|
||||
| mt \
|
||||
| msp430 \
|
||||
| nios | nios2 \
|
||||
| nds32 | nds32le | nds32be \
|
||||
| nios | nios2 | nios2eb | nios2el \
|
||||
| ns16k | ns32k \
|
||||
| or32 \
|
||||
| open8 | or1k | or1knd | or32 \
|
||||
| pdp10 | pdp11 | pj | pjl \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle \
|
||||
| pyramid \
|
||||
| riscv32 | riscv64 \
|
||||
| rl78 | rx \
|
||||
| score \
|
||||
| sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh64 | sh64le \
|
||||
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
|
||||
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
|
||||
| spu | strongarm \
|
||||
| tahoe | thumb | tic4x | tic80 | tron \
|
||||
| v850 | v850e \
|
||||
| spu \
|
||||
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
|
||||
| ubicom32 \
|
||||
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
|
||||
| visium \
|
||||
| we32k \
|
||||
| x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
|
||||
| z8k)
|
||||
| x86 | xc16x | xstormy16 | xtensa \
|
||||
| z8k | z80)
|
||||
basic_machine=$basic_machine-unknown
|
||||
;;
|
||||
m6811 | m68hc11 | m6812 | m68hc12)
|
||||
# Motorola 68HC11/12.
|
||||
c54x)
|
||||
basic_machine=tic54x-unknown
|
||||
;;
|
||||
c55x)
|
||||
basic_machine=tic55x-unknown
|
||||
;;
|
||||
c6x)
|
||||
basic_machine=tic6x-unknown
|
||||
;;
|
||||
leon|leon[3-9])
|
||||
basic_machine=sparc-$basic_machine
|
||||
;;
|
||||
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-none
|
||||
;;
|
||||
@ -300,6 +341,21 @@ case $basic_machine in
|
||||
basic_machine=mt-unknown
|
||||
;;
|
||||
|
||||
strongarm | thumb | xscale)
|
||||
basic_machine=arm-unknown
|
||||
;;
|
||||
xgate)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-none
|
||||
;;
|
||||
xscaleeb)
|
||||
basic_machine=armeb-unknown
|
||||
;;
|
||||
|
||||
xscaleel)
|
||||
basic_machine=armel-unknown
|
||||
;;
|
||||
|
||||
# We use `pc' rather than `unknown'
|
||||
# because (1) that's what they normally are, and
|
||||
# (2) the word "unknown" tends to confuse beginning users.
|
||||
@ -314,64 +370,89 @@ case $basic_machine in
|
||||
# Recognize the basic CPU types with company name.
|
||||
580-* \
|
||||
| a29k-* \
|
||||
| aarch64-* | aarch64_be-* \
|
||||
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
|
||||
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
|
||||
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
|
||||
| avr-* | avr32-* \
|
||||
| ba-* \
|
||||
| be32-* | be64-* \
|
||||
| bfin-* | bs2000-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
|
||||
| clipper-* | craynv-* | cydra-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* \
|
||||
| c8051-* | clipper-* | craynv-* | cydra-* \
|
||||
| d10v-* | d30v-* | dlx-* \
|
||||
| elxsi-* \
|
||||
| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
|
||||
| e2k-* | elxsi-* \
|
||||
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
|
||||
| h8300-* | h8500-* \
|
||||
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
|
||||
| hexagon-* \
|
||||
| i*86-* | i860-* | i960-* | ia64-* \
|
||||
| ip2k-* | iq2000-* \
|
||||
| k1om-* \
|
||||
| le32-* | le64-* \
|
||||
| lm32-* \
|
||||
| m32c-* | m32r-* | m32rle-* \
|
||||
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
|
||||
| m88110-* | m88k-* | maxq-* | mcore-* \
|
||||
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
|
||||
| microblaze-* | microblazeel-* \
|
||||
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
|
||||
| mips16-* \
|
||||
| mips64-* | mips64el-* \
|
||||
| mips64vr-* | mips64vrel-* \
|
||||
| mips64octeon-* | mips64octeonel-* \
|
||||
| mips64orion-* | mips64orionel-* \
|
||||
| mips64r5900-* | mips64r5900el-* \
|
||||
| mips64vr-* | mips64vrel-* \
|
||||
| mips64vr4100-* | mips64vr4100el-* \
|
||||
| mips64vr4300-* | mips64vr4300el-* \
|
||||
| mips64vr5000-* | mips64vr5000el-* \
|
||||
| mips64vr5900-* | mips64vr5900el-* \
|
||||
| mipsisa32-* | mipsisa32el-* \
|
||||
| mipsisa32r2-* | mipsisa32r2el-* \
|
||||
| mipsisa32r6-* | mipsisa32r6el-* \
|
||||
| mipsisa64-* | mipsisa64el-* \
|
||||
| mipsisa64r2-* | mipsisa64r2el-* \
|
||||
| mipsisa64r6-* | mipsisa64r6el-* \
|
||||
| mipsisa64sb1-* | mipsisa64sb1el-* \
|
||||
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
|
||||
| mipsr5900-* | mipsr5900el-* \
|
||||
| mipstx39-* | mipstx39el-* \
|
||||
| mmix-* \
|
||||
| mt-* \
|
||||
| msp430-* \
|
||||
| nios-* | nios2-* \
|
||||
| nds32-* | nds32le-* | nds32be-* \
|
||||
| nios-* | nios2-* | nios2eb-* | nios2el-* \
|
||||
| none-* | np1-* | ns16k-* | ns32k-* \
|
||||
| open8-* \
|
||||
| or1k*-* \
|
||||
| orion-* \
|
||||
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
||||
| pyramid-* \
|
||||
| romp-* | rs6000-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| riscv32-* | riscv64-* \
|
||||
| rl78-* | romp-* | rs6000-* | rx-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
|
||||
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
|
||||
| sparclite-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
|
||||
| tahoe-* | thumb-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
|
||||
| tahoe-* \
|
||||
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
|
||||
| tile*-* \
|
||||
| tron-* \
|
||||
| v850-* | v850e-* | vax-* \
|
||||
| ubicom32-* \
|
||||
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
|
||||
| vax-* \
|
||||
| visium-* \
|
||||
| we32k-* \
|
||||
| x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
|
||||
| xstormy16-* | xtensa-* \
|
||||
| x86-* | x86_64-* | xc16x-* | xps100-* \
|
||||
| xstormy16-* | xtensa*-* \
|
||||
| ymp-* \
|
||||
| z8k-*)
|
||||
| z8k-* | z80-*)
|
||||
;;
|
||||
# Recognize the basic CPU types without company name, with glob match.
|
||||
xtensa*)
|
||||
basic_machine=$basic_machine-unknown
|
||||
;;
|
||||
# Recognize the various machine names and aliases which stand
|
||||
# for a CPU type and a company and sometimes even an OS.
|
||||
@ -389,7 +470,7 @@ case $basic_machine in
|
||||
basic_machine=a29k-amd
|
||||
os=-udi
|
||||
;;
|
||||
abacus)
|
||||
abacus)
|
||||
basic_machine=abacus-unknown
|
||||
;;
|
||||
adobe68k)
|
||||
@ -435,6 +516,13 @@ case $basic_machine in
|
||||
basic_machine=m68k-apollo
|
||||
os=-bsd
|
||||
;;
|
||||
aros)
|
||||
basic_machine=i386-pc
|
||||
os=-aros
|
||||
;;
|
||||
asmjs)
|
||||
basic_machine=asmjs-unknown
|
||||
;;
|
||||
aux)
|
||||
basic_machine=m68k-apple
|
||||
os=-aux
|
||||
@ -443,10 +531,35 @@ case $basic_machine in
|
||||
basic_machine=ns32k-sequent
|
||||
os=-dynix
|
||||
;;
|
||||
blackfin)
|
||||
basic_machine=bfin-unknown
|
||||
os=-linux
|
||||
;;
|
||||
blackfin-*)
|
||||
basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
bluegene*)
|
||||
basic_machine=powerpc-ibm
|
||||
os=-cnk
|
||||
;;
|
||||
c54x-*)
|
||||
basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c55x-*)
|
||||
basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c6x-*)
|
||||
basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c90)
|
||||
basic_machine=c90-cray
|
||||
os=-unicos
|
||||
;;
|
||||
cegcc)
|
||||
basic_machine=arm-unknown
|
||||
os=-cegcc
|
||||
;;
|
||||
convex-c1)
|
||||
basic_machine=c1-convex
|
||||
os=-bsd
|
||||
@ -475,8 +588,8 @@ case $basic_machine in
|
||||
basic_machine=craynv-cray
|
||||
os=-unicosmp
|
||||
;;
|
||||
cr16c)
|
||||
basic_machine=cr16c-unknown
|
||||
cr16 | cr16-*)
|
||||
basic_machine=cr16-unknown
|
||||
os=-elf
|
||||
;;
|
||||
crds | unos)
|
||||
@ -514,6 +627,10 @@ case $basic_machine in
|
||||
basic_machine=m88k-motorola
|
||||
os=-sysv3
|
||||
;;
|
||||
dicos)
|
||||
basic_machine=i686-pc
|
||||
os=-dicos
|
||||
;;
|
||||
djgpp)
|
||||
basic_machine=i586-pc
|
||||
os=-msdosdjgpp
|
||||
@ -629,7 +746,6 @@ case $basic_machine in
|
||||
i370-ibm* | ibm*)
|
||||
basic_machine=i370-ibm
|
||||
;;
|
||||
# I'm not sure what "Sysv32" means. Should this be sysv3.2?
|
||||
i*86v32)
|
||||
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
|
||||
os=-sysv32
|
||||
@ -668,6 +784,17 @@ case $basic_machine in
|
||||
basic_machine=m68k-isi
|
||||
os=-sysv
|
||||
;;
|
||||
leon-*|leon[3-9]-*)
|
||||
basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
|
||||
;;
|
||||
m68knommu)
|
||||
basic_machine=m68k-unknown
|
||||
os=-linux
|
||||
;;
|
||||
m68knommu-*)
|
||||
basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
m88k-omron*)
|
||||
basic_machine=m88k-omron
|
||||
;;
|
||||
@ -679,10 +806,21 @@ case $basic_machine in
|
||||
basic_machine=ns32k-utek
|
||||
os=-sysv
|
||||
;;
|
||||
microblaze*)
|
||||
basic_machine=microblaze-xilinx
|
||||
;;
|
||||
mingw64)
|
||||
basic_machine=x86_64-pc
|
||||
os=-mingw64
|
||||
;;
|
||||
mingw32)
|
||||
basic_machine=i386-pc
|
||||
basic_machine=i686-pc
|
||||
os=-mingw32
|
||||
;;
|
||||
mingw32ce)
|
||||
basic_machine=arm-unknown
|
||||
os=-mingw32ce
|
||||
;;
|
||||
miniframe)
|
||||
basic_machine=m68000-convergent
|
||||
;;
|
||||
@ -704,6 +842,10 @@ case $basic_machine in
|
||||
basic_machine=powerpc-unknown
|
||||
os=-morphos
|
||||
;;
|
||||
moxiebox)
|
||||
basic_machine=moxie-unknown
|
||||
os=-moxiebox
|
||||
;;
|
||||
msdos)
|
||||
basic_machine=i386-pc
|
||||
os=-msdos
|
||||
@ -711,10 +853,18 @@ case $basic_machine in
|
||||
ms1-*)
|
||||
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
|
||||
;;
|
||||
msys)
|
||||
basic_machine=i686-pc
|
||||
os=-msys
|
||||
;;
|
||||
mvs)
|
||||
basic_machine=i370-ibm
|
||||
os=-mvs
|
||||
;;
|
||||
nacl)
|
||||
basic_machine=le32-unknown
|
||||
os=-nacl
|
||||
;;
|
||||
ncr3000)
|
||||
basic_machine=i486-ncr
|
||||
os=-sysv4
|
||||
@ -779,6 +929,12 @@ case $basic_machine in
|
||||
np1)
|
||||
basic_machine=np1-gould
|
||||
;;
|
||||
neo-tandem)
|
||||
basic_machine=neo-tandem
|
||||
;;
|
||||
nse-tandem)
|
||||
basic_machine=nse-tandem
|
||||
;;
|
||||
nsr-tandem)
|
||||
basic_machine=nsr-tandem
|
||||
;;
|
||||
@ -809,6 +965,14 @@ case $basic_machine in
|
||||
basic_machine=i860-intel
|
||||
os=-osf
|
||||
;;
|
||||
parisc)
|
||||
basic_machine=hppa-unknown
|
||||
os=-linux
|
||||
;;
|
||||
parisc-*)
|
||||
basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
pbd)
|
||||
basic_machine=sparc-tti
|
||||
;;
|
||||
@ -853,9 +1017,10 @@ case $basic_machine in
|
||||
;;
|
||||
power) basic_machine=power-ibm
|
||||
;;
|
||||
ppc) basic_machine=powerpc-unknown
|
||||
ppc | ppcbe) basic_machine=powerpc-unknown
|
||||
;;
|
||||
ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
ppc-* | ppcbe-*)
|
||||
basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
ppcle | powerpclittle | ppc-le | powerpc-little)
|
||||
basic_machine=powerpcle-unknown
|
||||
@ -880,7 +1045,11 @@ case $basic_machine in
|
||||
basic_machine=i586-unknown
|
||||
os=-pw32
|
||||
;;
|
||||
rdos)
|
||||
rdos | rdos64)
|
||||
basic_machine=x86_64-pc
|
||||
os=-rdos
|
||||
;;
|
||||
rdos32)
|
||||
basic_machine=i386-pc
|
||||
os=-rdos
|
||||
;;
|
||||
@ -925,6 +1094,9 @@ case $basic_machine in
|
||||
basic_machine=sh-hitachi
|
||||
os=-hms
|
||||
;;
|
||||
sh5el)
|
||||
basic_machine=sh5le-unknown
|
||||
;;
|
||||
sh64)
|
||||
basic_machine=sh64-unknown
|
||||
;;
|
||||
@ -946,6 +1118,9 @@ case $basic_machine in
|
||||
basic_machine=i860-stratus
|
||||
os=-sysv4
|
||||
;;
|
||||
strongarm-* | thumb-*)
|
||||
basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
sun2)
|
||||
basic_machine=m68000-sun
|
||||
;;
|
||||
@ -1002,17 +1177,9 @@ case $basic_machine in
|
||||
basic_machine=t90-cray
|
||||
os=-unicos
|
||||
;;
|
||||
tic54x | c54x*)
|
||||
basic_machine=tic54x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tic55x | c55x*)
|
||||
basic_machine=tic55x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tic6x | c6x*)
|
||||
basic_machine=tic6x-unknown
|
||||
os=-coff
|
||||
tile*)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-linux-gnu
|
||||
;;
|
||||
tx39)
|
||||
basic_machine=mipstx39-unknown
|
||||
@ -1081,6 +1248,9 @@ case $basic_machine in
|
||||
xps | xps100)
|
||||
basic_machine=xps100-honeywell
|
||||
;;
|
||||
xscale-* | xscalee[bl]-*)
|
||||
basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
|
||||
;;
|
||||
ymp)
|
||||
basic_machine=ymp-cray
|
||||
os=-unicos
|
||||
@ -1089,6 +1259,10 @@ case $basic_machine in
|
||||
basic_machine=z8k-unknown
|
||||
os=-sim
|
||||
;;
|
||||
z80-*-coff)
|
||||
basic_machine=z80-unknown
|
||||
os=-sim
|
||||
;;
|
||||
none)
|
||||
basic_machine=none-none
|
||||
os=-none
|
||||
@ -1127,7 +1301,7 @@ case $basic_machine in
|
||||
we32k)
|
||||
basic_machine=we32k-att
|
||||
;;
|
||||
sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
|
||||
sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
|
||||
basic_machine=sh-unknown
|
||||
;;
|
||||
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
|
||||
@ -1174,9 +1348,12 @@ esac
|
||||
if [ x"$os" != x"" ]
|
||||
then
|
||||
case $os in
|
||||
# First match some system type aliases
|
||||
# that might get confused with valid system types.
|
||||
# First match some system type aliases
|
||||
# that might get confused with valid system types.
|
||||
# -solaris* is a basic system type, with this one exception.
|
||||
-auroraux)
|
||||
os=-auroraux
|
||||
;;
|
||||
-solaris1 | -solaris1.*)
|
||||
os=`echo $os | sed -e 's|solaris1|sunos4|'`
|
||||
;;
|
||||
@ -1197,29 +1374,32 @@ case $os in
|
||||
# Each alternative MUST END IN A *, to match a version number.
|
||||
# -sysv* is not here because it comes later, after sysvr4.
|
||||
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
|
||||
| -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
|
||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
|
||||
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
|
||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
|
||||
| -sym* | -kopensolaris* | -plan9* \
|
||||
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
|
||||
| -aos* \
|
||||
| -aos* | -aros* | -cloudabi* | -sortix* \
|
||||
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
||||
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
|
||||
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
|
||||
| -openbsd* | -solidbsd* \
|
||||
| -bitrig* | -openbsd* | -solidbsd* \
|
||||
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
|
||||
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
|
||||
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
|
||||
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
|
||||
| -chorusos* | -chorusrdb* \
|
||||
| -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* \
|
||||
| -chorusos* | -chorusrdb* | -cegcc* \
|
||||
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
|
||||
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
|
||||
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
||||
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
|
||||
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
|
||||
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||
| -skyos* | -haiku* | -rdos* | -toppers*)
|
||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
|
||||
| -onefs* | -tirtos*)
|
||||
# Remember, each alternative MUST END IN *, to match a version number.
|
||||
;;
|
||||
-qnx*)
|
||||
@ -1258,7 +1438,7 @@ case $os in
|
||||
-opened*)
|
||||
os=-openedition
|
||||
;;
|
||||
-os400*)
|
||||
-os400*)
|
||||
os=-os400
|
||||
;;
|
||||
-wince*)
|
||||
@ -1307,7 +1487,7 @@ case $os in
|
||||
-sinix*)
|
||||
os=-sysv4
|
||||
;;
|
||||
-tpf*)
|
||||
-tpf*)
|
||||
os=-tpf
|
||||
;;
|
||||
-triton*)
|
||||
@ -1343,12 +1523,14 @@ case $os in
|
||||
-aros*)
|
||||
os=-aros
|
||||
;;
|
||||
-kaos*)
|
||||
os=-kaos
|
||||
;;
|
||||
-zvmoe)
|
||||
os=-zvmoe
|
||||
;;
|
||||
-dicos*)
|
||||
os=-dicos
|
||||
;;
|
||||
-nacl*)
|
||||
;;
|
||||
-none)
|
||||
;;
|
||||
*)
|
||||
@ -1371,10 +1553,10 @@ else
|
||||
# system, and we'll never get to this point.
|
||||
|
||||
case $basic_machine in
|
||||
score-*)
|
||||
score-*)
|
||||
os=-elf
|
||||
;;
|
||||
spu-*)
|
||||
spu-*)
|
||||
os=-elf
|
||||
;;
|
||||
*-acorn)
|
||||
@ -1386,8 +1568,23 @@ case $basic_machine in
|
||||
arm*-semi)
|
||||
os=-aout
|
||||
;;
|
||||
c4x-* | tic4x-*)
|
||||
os=-coff
|
||||
c4x-* | tic4x-*)
|
||||
os=-coff
|
||||
;;
|
||||
c8051-*)
|
||||
os=-elf
|
||||
;;
|
||||
hexagon-*)
|
||||
os=-elf
|
||||
;;
|
||||
tic54x-*)
|
||||
os=-coff
|
||||
;;
|
||||
tic55x-*)
|
||||
os=-coff
|
||||
;;
|
||||
tic6x-*)
|
||||
os=-coff
|
||||
;;
|
||||
# This must come before the *-dec entry.
|
||||
pdp10-*)
|
||||
@ -1407,13 +1604,13 @@ case $basic_machine in
|
||||
;;
|
||||
m68000-sun)
|
||||
os=-sunos3
|
||||
# This also exists in the configure program, but was not the
|
||||
# default.
|
||||
# os=-sunos4
|
||||
;;
|
||||
m68*-cisco)
|
||||
os=-aout
|
||||
;;
|
||||
mep-*)
|
||||
os=-elf
|
||||
;;
|
||||
mips*-cisco)
|
||||
os=-elf
|
||||
;;
|
||||
@ -1438,7 +1635,7 @@ case $basic_machine in
|
||||
*-ibm)
|
||||
os=-aix
|
||||
;;
|
||||
*-knuth)
|
||||
*-knuth)
|
||||
os=-mmixware
|
||||
;;
|
||||
*-wec)
|
||||
@ -1543,7 +1740,7 @@ case $basic_machine in
|
||||
-sunos*)
|
||||
vendor=sun
|
||||
;;
|
||||
-aix*)
|
||||
-cnk*|-aix*)
|
||||
vendor=ibm
|
||||
;;
|
||||
-beos*)
|
||||
|
@ -1,10 +1,9 @@
|
||||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2006-10-15.18
|
||||
scriptversion=2013-05-30.07; # UTC
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 Free Software
|
||||
# Foundation, Inc.
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -17,9 +16,7 @@ scriptversion=2006-10-15.18
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
@ -30,9 +27,9 @@ scriptversion=2006-10-15.18
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try \`$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
@ -42,11 +39,11 @@ as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by `PROGRAMS ARGS'.
|
||||
object Object file output by `PROGRAMS ARGS'.
|
||||
source Source file read by 'PROGRAMS ARGS'.
|
||||
object Object file output by 'PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputing dependencies.
|
||||
tmpdepfile Temporary file to use when outputting dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
@ -59,6 +56,66 @@ EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
# Get the directory component of the given path, and save it in the
|
||||
# global variables '$dir'. Note that this directory component will
|
||||
# be either empty or ending with a '/' character. This is deliberate.
|
||||
set_dir_from ()
|
||||
{
|
||||
case $1 in
|
||||
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
|
||||
*) dir=;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Get the suffix-stripped basename of the given path, and save it the
|
||||
# global variable '$base'.
|
||||
set_base_from ()
|
||||
{
|
||||
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
|
||||
}
|
||||
|
||||
# If no dependency file was actually created by the compiler invocation,
|
||||
# we still have to create a dummy depfile, to avoid errors with the
|
||||
# Makefile "include basename.Plo" scheme.
|
||||
make_dummy_depfile ()
|
||||
{
|
||||
echo "#dummy" > "$depfile"
|
||||
}
|
||||
|
||||
# Factor out some common post-processing of the generated depfile.
|
||||
# Requires the auxiliary global variable '$tmpdepfile' to be set.
|
||||
aix_post_process_depfile ()
|
||||
{
|
||||
# If the compiler actually managed to produce a dependency file,
|
||||
# post-process it.
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form 'foo.o: dependency.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# $object: dependency.h
|
||||
# and one to simply output
|
||||
# dependency.h:
|
||||
# which is needed to avoid the deleted-header problem.
|
||||
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
|
||||
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
|
||||
} > "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
}
|
||||
|
||||
# A tabulation character.
|
||||
tab=' '
|
||||
# A newline character.
|
||||
nl='
|
||||
'
|
||||
# Character ranges might be problematic outside the C locale.
|
||||
# These definitions help.
|
||||
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
lower=abcdefghijklmnopqrstuvwxyz
|
||||
digits=0123456789
|
||||
alpha=${upper}${lower}
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
@ -71,6 +128,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Avoid interferences from the environment.
|
||||
gccflag= dashmflag=
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
@ -82,9 +142,32 @@ if test "$depmode" = hp; then
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
cygpath_u="cygpath -u -f -"
|
||||
if test "$depmode" = msvcmsys; then
|
||||
# This is just like msvisualcpp but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvisualcpp
|
||||
fi
|
||||
|
||||
if test "$depmode" = msvc7msys; then
|
||||
# This is just like msvc7 but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvc7
|
||||
fi
|
||||
|
||||
if test "$depmode" = xlc; then
|
||||
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
|
||||
gccflag=-qmakedep=gcc,-MF
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
@ -107,8 +190,7 @@ gcc3)
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
@ -116,13 +198,17 @@ gcc3)
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
|
||||
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
|
||||
## (see the conditional assignment to $gccflag above).
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say).
|
||||
## -MM, not -M (despite what the docs say). Also, it might not be
|
||||
## supported by the other compilers which use the 'gcc' depmode.
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
@ -130,31 +216,31 @@ gcc)
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
# The second -e expression handles DOS-style file names with drive
|
||||
# letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the `deleted header file' problem.
|
||||
## This next piece of magic avoids the "deleted header file" problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the `:'. On the theory
|
||||
## Some versions of gcc put a space before the ':'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well.
|
||||
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||
## to the object. Take care to not repeat it in the output.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
@ -172,8 +258,7 @@ sgi)
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
@ -181,99 +266,156 @@ sgi)
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like `#:fec' to the end of the
|
||||
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr '
|
||||
' ' ' >> $depfile
|
||||
echo >> $depfile
|
||||
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
||||
| tr "$nl" ' ' >> "$depfile"
|
||||
echo >> "$depfile"
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> $depfile
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
make_dummy_depfile
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
xlc)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts `$object:' at the
|
||||
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
|
||||
tmpdepfile="$stripped.u"
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
|
||||
if test -f "$tmpdepfile"; then :
|
||||
else
|
||||
stripped=`echo "$stripped" | sed 's,^.*/,,'`
|
||||
tmpdepfile="$stripped.u"
|
||||
fi
|
||||
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
if test -f "$tmpdepfile"; then
|
||||
outname="$stripped.o"
|
||||
# Each line is of the form `foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
tcc)
|
||||
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
|
||||
# FIXME: That version still under development at the moment of writing.
|
||||
# Make that this statement remains true also for stable, released
|
||||
# versions.
|
||||
# It will wrap lines (doesn't matter whether long or short) with a
|
||||
# trailing '\', as in:
|
||||
#
|
||||
# foo.o : \
|
||||
# foo.c \
|
||||
# foo.h \
|
||||
#
|
||||
# It will put a trailing '\' even on the last line, and will use leading
|
||||
# spaces rather than leading tabs (at least since its commit 0394caf7
|
||||
# "Emit spaces for -MD").
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
|
||||
# We have to change lines of the first kind to '$object: \'.
|
||||
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
|
||||
# And for each line of the second kind, we have to emit a 'dep.h:'
|
||||
# dummy dependency, to avoid the deleted-header problem.
|
||||
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
icc)
|
||||
# Intel's C compiler understands `-MD -MF file'. However on
|
||||
# icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
||||
# ICC 7.0 will fill foo.d with something like
|
||||
# foo.o: sub/foo.c
|
||||
# foo.o: sub/foo.h
|
||||
# which is wrong. We want:
|
||||
# sub/foo.o: sub/foo.c
|
||||
# sub/foo.o: sub/foo.h
|
||||
# sub/foo.c:
|
||||
# sub/foo.h:
|
||||
# ICC 7.1 will output
|
||||
## The order of this option in the case statement is important, since the
|
||||
## shell code in configure will try each of these formats in the order
|
||||
## listed in this file. A plain '-MD' option would be understood by many
|
||||
## compilers, so we must ensure this comes after the gcc and icc options.
|
||||
pgcc)
|
||||
# Portland's C compiler understands '-MD'.
|
||||
# Will always output deps to 'file.d' where file is the root name of the
|
||||
# source file under compilation, even if file resides in a subdirectory.
|
||||
# The object file name does not affect the name of the '.d' file.
|
||||
# pgcc 10.2 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using \ :
|
||||
# and will wrap long lines using '\' :
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
set_dir_from "$object"
|
||||
# Use the source, not the object, to determine the base name, since
|
||||
# that's sadly what pgcc will do too.
|
||||
set_base_from "$source"
|
||||
tmpdepfile=$base.d
|
||||
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
# For projects that build the same source file twice into different object
|
||||
# files, the pgcc approach of using the *source* file root name can cause
|
||||
# problems in parallel builds. Use a locking strategy to avoid stomping on
|
||||
# the same $tmpdepfile.
|
||||
lockdir=$base.d-lock
|
||||
trap "
|
||||
echo '$0: caught signal, cleaning up...' >&2
|
||||
rmdir '$lockdir'
|
||||
exit 1
|
||||
" 1 2 13 15
|
||||
numtries=100
|
||||
i=$numtries
|
||||
while test $i -gt 0; do
|
||||
# mkdir is a portable test-and-set.
|
||||
if mkdir "$lockdir" 2>/dev/null; then
|
||||
# This process acquired the lock.
|
||||
"$@" -MD
|
||||
stat=$?
|
||||
# Release the lock.
|
||||
rmdir "$lockdir"
|
||||
break
|
||||
else
|
||||
# If the lock is being held by a different process, wait
|
||||
# until the winning process is done or we timeout.
|
||||
while test -d "$lockdir" && test $i -gt 0; do
|
||||
sleep 1
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
fi
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
trap - 1 2 13 15
|
||||
if test $i -le 0; then
|
||||
echo "$0: failed to acquire lock after $numtries attempts" >&2
|
||||
echo "$0: check lockdir '$lockdir'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
@ -285,8 +427,8 @@ icc)
|
||||
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
|
||||
sed -e 's/$/ :/' >> "$depfile"
|
||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
@ -297,9 +439,8 @@ hp2)
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
@ -310,8 +451,7 @@ hp2)
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
@ -321,72 +461,107 @@ hp2)
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add `dependent.h:' lines.
|
||||
sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile"
|
||||
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add 'dependent.h:' lines.
|
||||
sed -ne '2,${
|
||||
s/^ *//
|
||||
s/ \\*$//
|
||||
s/$/:/
|
||||
p
|
||||
}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
make_dummy_depfile
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in `foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in 'foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# With Tru64 cc, shared objects can also be used to make a
|
||||
# static library. This mechanism is used in libtool 1.4 series to
|
||||
# handle both shared and static libraries in a single compilation.
|
||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
|
||||
#
|
||||
# With libtool 1.5 this exception was removed, and libtool now
|
||||
# generates 2 separate objects for the 2 libraries. These two
|
||||
# compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.o.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
tmpdepfile4=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
if test "$libtool" = yes; then
|
||||
# Libtool generates 2 separate objects for the 2 libraries. These
|
||||
# two compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
|
||||
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
exit $stat
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
# That's a tab and a space in the [].
|
||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
# Same post-processing that is required for AIX mode.
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
msvc7)
|
||||
if test "$libtool" = yes; then
|
||||
showIncludes=-Wc,-showIncludes
|
||||
else
|
||||
showIncludes=-showIncludes
|
||||
fi
|
||||
"$@" $showIncludes > "$tmpdepfile"
|
||||
stat=$?
|
||||
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The first sed program below extracts the file names and escapes
|
||||
# backslashes for cygpath. The second sed program outputs the file
|
||||
# name when reading, but also accumulates all include files in the
|
||||
# hold buffer in order to output them again at the end. This only
|
||||
# works with sed implementations that can handle large buffers.
|
||||
sed < "$tmpdepfile" -n '
|
||||
/^Note: including file: *\(.*\)/ {
|
||||
s//\1/
|
||||
s/\\/\\\\/g
|
||||
p
|
||||
}' | $cygpath_u | sort -u | sed -n '
|
||||
s/ /\\ /g
|
||||
s/\(.*\)/'"$tab"'\1 \\/p
|
||||
s/.\(.*\) \\/\1:/
|
||||
H
|
||||
$ {
|
||||
s/.*/'"$tab"'/
|
||||
G
|
||||
p
|
||||
}' >> "$depfile"
|
||||
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7msys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
@ -399,13 +574,13 @@ dashmstdout)
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test $1 != '--mode=compile'; do
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove `-o $object'.
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
@ -425,18 +600,18 @@ dashmstdout)
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for `:'
|
||||
# Require at least two characters before searching for ':'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
|
||||
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this sed invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
@ -450,41 +625,51 @@ makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test $1 != '--mode=compile'; do
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no
|
||||
for arg in "$@"; do
|
||||
cleared=no eat=no
|
||||
for arg
|
||||
do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
if test $eat = yes; then
|
||||
eat=no
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-arch)
|
||||
eat=yes ;;
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix="`echo $object | sed 's/^.*\././'`"
|
||||
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' '
|
||||
' | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
# makedepend may prepend the VPATH from the source file name to the object.
|
||||
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process the last invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed '1,2d' "$tmpdepfile" \
|
||||
| tr ' ' "$nl" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
@ -495,13 +680,13 @@ cpp)
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test $1 != '--mode=compile'; do
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove `-o $object'.
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
@ -520,10 +705,10 @@ cpp)
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E |
|
||||
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
||||
sed '$ s: \\$::' > "$tmpdepfile"
|
||||
"$@" -E \
|
||||
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
| sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
@ -533,35 +718,56 @@ cpp)
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o,
|
||||
# because we must use -o when running libtool.
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
|
||||
"$@" -E 2>/dev/null |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
|
||||
echo " " >> "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||
echo "$tab" >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvcmsys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
@ -580,5 +786,6 @@ exit 0
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2006-10-14.15
|
||||
scriptversion=2013-12-25.23; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
@ -35,57 +35,57 @@ scriptversion=2006-10-14.15
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# 'make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" "" $nl"
|
||||
IFS=" $tab$nl"
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_glob=
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
chgrpcmd=
|
||||
stripcmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dstarg=
|
||||
no_target_directory=
|
||||
dst_arg=
|
||||
|
||||
usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
@ -95,91 +95,101 @@ In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
-c (ignored)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) shift
|
||||
continue;;
|
||||
-c) ;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
shift
|
||||
shift
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
continue;;
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog
|
||||
shift
|
||||
continue;;
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t) dstarg=$2
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) no_target_directory=true
|
||||
shift
|
||||
continue;;
|
||||
-T) is_target_a_directory=never;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dstarg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dstarg"
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dstarg=$arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
@ -188,13 +198,26 @@ if test $# -eq 0; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# It's OK to call 'install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
trap '(exit $?); exit' 1 2 13 15
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
@ -205,16 +228,16 @@ if test -z "$dir_arg"; then
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
@ -222,9 +245,9 @@ fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names starting with `-'.
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $src in
|
||||
-*) src=./$src ;;
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
@ -242,55 +265,24 @@ do
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dstarg"; then
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dst=$dstarg
|
||||
# Protect names starting with `-'.
|
||||
case $dst in
|
||||
-*) dst=./$dst ;;
|
||||
esac
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dstarg: Is a directory" >&2
|
||||
exit 1
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
dstdir=`dirname "$dst"`
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
@ -301,74 +293,74 @@ do
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writeable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
@ -378,60 +370,51 @@ do
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix=/ ;;
|
||||
-*) prefix=./ ;;
|
||||
*) prefix= ;;
|
||||
esac
|
||||
|
||||
case $posix_glob in
|
||||
'')
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=true
|
||||
else
|
||||
posix_glob=false
|
||||
fi ;;
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
$posix_glob && set -f
|
||||
set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
$posix_glob && set +f
|
||||
set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test -z "$d" && continue
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@ -459,41 +442,51 @@ do
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
|
||||
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
|
||||
&& { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
|
||||
&& { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
{ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \
|
||||
|| {
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
if test -f "$dst"; then
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null \
|
||||
|| { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \
|
||||
&& { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\
|
||||
|| {
|
||||
echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
else
|
||||
:
|
||||
fi
|
||||
} &&
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
} || exit 1
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
@ -503,5 +496,6 @@ done
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
8505
autotools/ltmain.sh
8505
autotools/ltmain.sh
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,10 @@
|
||||
#! /bin/sh
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
# Common wrapper for a few potentially missing GNU programs.
|
||||
|
||||
scriptversion=2006-05-10.23
|
||||
scriptversion=2013-10-28.13; # UTC
|
||||
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006
|
||||
# Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -18,9 +17,7 @@ scriptversion=2006-05-10.23
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
@ -28,66 +25,40 @@ scriptversion=2006-05-10.23
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
|
||||
sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
msg="missing on your system"
|
||||
|
||||
case $1 in
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
# Exit code 63 means version mismatch. This often happens
|
||||
# when the user try to use an ancient version of a tool on
|
||||
# a file that requires a minimum version. In this case we
|
||||
# we should proceed has if the program had been absent, or
|
||||
# if --run hadn't been passed.
|
||||
if test $? = 63; then
|
||||
run=:
|
||||
msg="probably too old"
|
||||
fi
|
||||
;;
|
||||
|
||||
--is-lightweight)
|
||||
# Used by our autoconf macros to check whether the available missing
|
||||
# script is modern enough.
|
||||
exit 0
|
||||
;;
|
||||
|
||||
--run)
|
||||
# Back-compat with the calling convention used by older automake.
|
||||
shift
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
|
||||
to PROGRAM being missing or too old.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
autom4te touch the output file, or create a stub one
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
tar try tar, gnutar, gtar, then tar without non-portable flags
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
aclocal autoconf autoheader autom4te automake makeinfo
|
||||
bison yacc flex lex help2man
|
||||
|
||||
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
|
||||
'g' are ignored when checking the name.
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
@ -99,269 +70,146 @@ Send bug reports to <bug-automake@gnu.org>."
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: Unknown \`$1' option"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
echo 1>&2 "$0: unknown '$1' option"
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# Now exit if we have it, but it failed. Also exit now if we
|
||||
# don't have it and --version was passed (most likely to detect
|
||||
# the program).
|
||||
case $1 in
|
||||
lex|yacc)
|
||||
# Not GNU programs, they don't have --version.
|
||||
# Run the given program, remember its exit status.
|
||||
"$@"; st=$?
|
||||
|
||||
# If it succeeded, we are done.
|
||||
test $st -eq 0 && exit 0
|
||||
|
||||
# Also exit now if we it failed (or wasn't found), and '--version' was
|
||||
# passed; such an option is passed most likely to detect whether the
|
||||
# program is present and works.
|
||||
case $2 in --version|--help) exit $st;; esac
|
||||
|
||||
# Exit code 63 means version mismatch. This often happens when the user
|
||||
# tries to use an ancient version of a tool on a file that requires a
|
||||
# minimum version.
|
||||
if test $st -eq 63; then
|
||||
msg="probably too old"
|
||||
elif test $st -eq 127; then
|
||||
# Program was missing.
|
||||
msg="missing on your system"
|
||||
else
|
||||
# Program was found and executed, but failed. Give up.
|
||||
exit $st
|
||||
fi
|
||||
|
||||
perl_URL=http://www.perl.org/
|
||||
flex_URL=http://flex.sourceforge.net/
|
||||
gnu_software_URL=http://www.gnu.org/software
|
||||
|
||||
program_details ()
|
||||
{
|
||||
case $1 in
|
||||
aclocal|automake)
|
||||
echo "The '$1' program is part of the GNU Automake package:"
|
||||
echo "<$gnu_software_URL/automake>"
|
||||
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/autoconf>"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
autoconf|autom4te|autoheader)
|
||||
echo "The '$1' program is part of the GNU Autoconf package:"
|
||||
echo "<$gnu_software_URL/autoconf/>"
|
||||
echo "It also requires GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
give_advice ()
|
||||
{
|
||||
# Normalize program name to check for.
|
||||
normalized_program=`echo "$1" | sed '
|
||||
s/^gnu-//; t
|
||||
s/^gnu//; t
|
||||
s/^g//; t'`
|
||||
|
||||
printf '%s\n' "'$1' is $msg."
|
||||
|
||||
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
|
||||
case $normalized_program in
|
||||
autoconf*)
|
||||
echo "You should only need it if you modified 'configure.ac',"
|
||||
echo "or m4 files included by it."
|
||||
program_details 'autoconf'
|
||||
;;
|
||||
autoheader*)
|
||||
echo "You should only need it if you modified 'acconfig.h' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'autoheader'
|
||||
;;
|
||||
automake*)
|
||||
echo "You should only need it if you modified 'Makefile.am' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'automake'
|
||||
;;
|
||||
aclocal*)
|
||||
echo "You should only need it if you modified 'acinclude.m4' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'aclocal'
|
||||
;;
|
||||
autom4te*)
|
||||
echo "You might have modified some maintainer files that require"
|
||||
echo "the 'autom4te' program to be rebuilt."
|
||||
program_details 'autom4te'
|
||||
;;
|
||||
bison*|yacc*)
|
||||
echo "You should only need it if you modified a '.y' file."
|
||||
echo "You may want to install the GNU Bison package:"
|
||||
echo "<$gnu_software_URL/bison/>"
|
||||
;;
|
||||
lex*|flex*)
|
||||
echo "You should only need it if you modified a '.l' file."
|
||||
echo "You may want to install the Fast Lexical Analyzer package:"
|
||||
echo "<$flex_URL>"
|
||||
;;
|
||||
help2man*)
|
||||
echo "You should only need it if you modified a dependency" \
|
||||
"of a man page."
|
||||
echo "You may want to install the GNU Help2man package:"
|
||||
echo "<$gnu_software_URL/help2man/>"
|
||||
;;
|
||||
makeinfo*)
|
||||
echo "You should only need it if you modified a '.texi' file, or"
|
||||
echo "any other file indirectly affecting the aspect of the manual."
|
||||
echo "You might want to install the Texinfo package:"
|
||||
echo "<$gnu_software_URL/texinfo/>"
|
||||
echo "The spurious makeinfo call might also be the consequence of"
|
||||
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
|
||||
echo "want to install GNU make:"
|
||||
echo "<$gnu_software_URL/make/>"
|
||||
;;
|
||||
*)
|
||||
echo "You might have modified some files without having the proper"
|
||||
echo "tools for further handling them. Check the 'README' file, it"
|
||||
echo "often tells you about the needed prerequisites for installing"
|
||||
echo "this package. You may also peek at any GNU archive site, in"
|
||||
echo "case some other package contains this missing '$1' program."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
tar)
|
||||
if test -n "$run"; then
|
||||
echo 1>&2 "ERROR: \`tar' requires --run"
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
give_advice "$1" | sed -e '1s/^/WARNING: /' \
|
||||
-e '2,$s/^/ /' >&2
|
||||
|
||||
*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
# Could not run --version or --help. This is probably someone
|
||||
# running `$TOOL --version' or `$TOOL --help' to check whether
|
||||
# $TOOL exists and not knowing $TOOL uses missing.
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case $1 in
|
||||
aclocal*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case $f in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, but is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison|yacc)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' $msg. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG="\${$#}"
|
||||
case $LASTARG in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f y.tab.h; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if test ! -f y.tab.c; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex|flex)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG="\${$#}"
|
||||
case $LASTARG in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f lex.yy.c; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
# The file to touch is that specified with -o ...
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -z "$file"; then
|
||||
# ... or it is the one specified with @setfilename ...
|
||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '
|
||||
/^@setfilename/{
|
||||
s/.* \([^ ]*\) *$/\1/
|
||||
p
|
||||
q
|
||||
}' $infile`
|
||||
# ... or it is derived from the source name (dir/f.texi becomes f.info)
|
||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
|
||||
fi
|
||||
# If the file does not exist, the user really needs makeinfo;
|
||||
# let's fail without touching anything.
|
||||
test -f $file || exit 1
|
||||
touch $file
|
||||
;;
|
||||
|
||||
tar)
|
||||
shift
|
||||
|
||||
# We have already tried tar in the generic part.
|
||||
# Look for gnutar/gtar before invocation to avoid ugly error
|
||||
# messages.
|
||||
if (gnutar --version > /dev/null 2>&1); then
|
||||
gnutar "$@" && exit 0
|
||||
fi
|
||||
if (gtar --version > /dev/null 2>&1); then
|
||||
gtar "$@" && exit 0
|
||||
fi
|
||||
firstarg="$1"
|
||||
if shift; then
|
||||
case $firstarg in
|
||||
*o*)
|
||||
firstarg=`echo "$firstarg" | sed s/o//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
case $firstarg in
|
||||
*h*)
|
||||
firstarg=`echo "$firstarg" | sed s/h//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: I can't seem to be able to run \`tar' with the given arguments.
|
||||
You may want to install GNU tar or Free paxutils, or check the
|
||||
command line arguments."
|
||||
exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequisites for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
# Propagate the correct exit status (expected to be 127 for a program
|
||||
# not found, 63 for a program that failed due to version mismatch).
|
||||
exit $st
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
148
autotools/test-driver
Executable file
148
autotools/test-driver
Executable file
@ -0,0 +1,148 @@
|
||||
#! /bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
scriptversion=2013-07-13.22; # UTC
|
||||
|
||||
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$0: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--]
|
||||
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=no
|
||||
color_tests=no
|
||||
enable_hard_errors=yes
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
missing_opts=
|
||||
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
|
||||
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
|
||||
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
|
||||
if test x"$missing_opts" != x; then
|
||||
usage_error "the following mandatory options are missing:$missing_opts"
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
usage_error "missing argument"
|
||||
fi
|
||||
|
||||
if test $color_tests = yes; then
|
||||
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||
red='[0;31m' # Red.
|
||||
grn='[0;32m' # Green.
|
||||
lgn='[1;32m' # Light green.
|
||||
blu='[1;34m' # Blue.
|
||||
mgn='[0;35m' # Magenta.
|
||||
std='[m' # No color.
|
||||
else
|
||||
red= grn= lgn= blu= mgn= std=
|
||||
fi
|
||||
|
||||
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||
trap "st=129; $do_exit" 1
|
||||
trap "st=130; $do_exit" 2
|
||||
trap "st=141; $do_exit" 13
|
||||
trap "st=143; $do_exit" 15
|
||||
|
||||
# Test script is run here.
|
||||
"$@" >$log_file 2>&1
|
||||
estatus=$?
|
||||
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
tweaked_estatus=1
|
||||
else
|
||||
tweaked_estatus=$estatus
|
||||
fi
|
||||
|
||||
case $tweaked_estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report the test outcome and exit status in the logs, so that one can
|
||||
# know whether the test passed or failed simply by looking at the '.log'
|
||||
# file, without the need of also peaking into the corresponding '.trs'
|
||||
# file (automake bug#11814).
|
||||
echo "$res $test_name (exit status: $estatus)" >>$log_file
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
# Register the test result, and other relevant metadata.
|
||||
echo ":test-result: $res" > $trs_file
|
||||
echo ":global-test-result: $res" >> $trs_file
|
||||
echo ":recheck: $recheck" >> $trs_file
|
||||
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
10
breakpad-client.pc.in
Normal file
10
breakpad-client.pc.in
Normal file
@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/@PACKAGE_NAME@
|
||||
|
||||
Name: google-breakpad-client
|
||||
Description: An open-source multi-platform crash reporting system
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lbreakpad_client @PTHREAD_LIBS@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@
|
10
breakpad.pc.in
Normal file
10
breakpad.pc.in
Normal file
@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/@PACKAGE_NAME@
|
||||
|
||||
Name: google-breakpad
|
||||
Description: An open-source multi-platform crash reporting system
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lbreakpad @PTHREAD_LIBS@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@
|
5
codereview.settings
Normal file
5
codereview.settings
Normal file
@ -0,0 +1,5 @@
|
||||
# This file is used by gcl to get repository specific information.
|
||||
CODE_REVIEW_SERVER: codereview.chromium.org
|
||||
CC_LIST: google-breakpad-dev@googlegroups.com
|
||||
TRY_ON_UPLOAD: False
|
||||
VIEW_VC: https://chromium.googlesource.com/breakpad/breakpad/+/
|
146
configure.ac
146
configure.ac
@ -30,22 +30,151 @@
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(breakpad, 0.1, opensource@google.com)
|
||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
|
||||
dnl Sanity check: the argument is just a file that should exist.
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_SRCDIR(README.md)
|
||||
AC_CONFIG_AUX_DIR(autotools)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
AM_INIT_AUTOMAKE(subdir-objects tar-ustar)
|
||||
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
|
||||
AM_CONFIG_HEADER(src/config.h)
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
AM_PROG_AR
|
||||
AM_PROG_AS
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_CPP
|
||||
AC_PROG_CXX
|
||||
AC_PROG_RANLIB
|
||||
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
|
||||
|
||||
AC_PROG_LIBTOOL
|
||||
AC_SUBST(LIBTOOL_DEPS)
|
||||
dnl This must come before all the feature tests below.
|
||||
AC_ARG_ENABLE(m32,
|
||||
AS_HELP_STRING([--enable-m32],
|
||||
[Compile/build with -m32]
|
||||
[(default is no)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
CFLAGS="${CFLAGS} -m32"
|
||||
CXXFLAGS="${CXXFLAGS} -m32"
|
||||
usem32=true
|
||||
;;
|
||||
no)
|
||||
usem32=false
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --enable-m32)
|
||||
;;
|
||||
esac],
|
||||
[usem32=false])
|
||||
|
||||
AC_HEADER_STDC
|
||||
AC_SYS_LARGEFILE
|
||||
m4_include(m4/ax_pthread.m4)
|
||||
AX_PTHREAD
|
||||
AC_CHECK_HEADERS([a.out.h])
|
||||
|
||||
m4_include(m4/ax_cxx_compile_stdcxx.m4)
|
||||
AX_CXX_COMPILE_STDCXX(11, noext, mandatory)
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
LINUX_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue)
|
||||
|
||||
# Only use Android support headers when compiling for Android
|
||||
case $host in
|
||||
*-android*)
|
||||
ANDROID_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue)
|
||||
|
||||
# Some tools (like mac ones) only support x86 currently.
|
||||
case $host_cpu in
|
||||
i?86|x86_64)
|
||||
X86_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(X86_HOST, test x$X86_HOST = xtrue)
|
||||
|
||||
AC_ARG_ENABLE(processor,
|
||||
AS_HELP_STRING([--disable-processor],
|
||||
[Don't build processor library]
|
||||
[(default is no)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
disable_processor=false
|
||||
;;
|
||||
no)
|
||||
disable_processor=true
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --disable-processor)
|
||||
;;
|
||||
esac],
|
||||
[disable_processor=false])
|
||||
AM_CONDITIONAL(DISABLE_PROCESSOR, test x$disable_processor = xtrue)
|
||||
|
||||
AC_ARG_ENABLE(tools,
|
||||
AS_HELP_STRING([--disable-tools],
|
||||
[Don't build tool binaries]
|
||||
[(default is no)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
disable_tools=false
|
||||
;;
|
||||
no)
|
||||
disable_tools=true
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --disable-tools)
|
||||
;;
|
||||
esac],
|
||||
[disable_tools=false])
|
||||
AM_CONDITIONAL(DISABLE_TOOLS, test x$disable_tools = xtrue)
|
||||
|
||||
if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then
|
||||
AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(system-test-libs,
|
||||
AS_HELP_STRING([--enable-system-test-libs],
|
||||
[Use gtest/gmock/etc... from the system instead ]
|
||||
[of the local copies (default is local)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
system_test_libs=true
|
||||
;;
|
||||
no)
|
||||
system_test_libs=false
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --enable-system-test-libs)
|
||||
;;
|
||||
esac],
|
||||
[system_test_libs=false])
|
||||
AM_CONDITIONAL(SYSTEM_TEST_LIBS, test x$system_test_libs = xtrue)
|
||||
|
||||
AC_ARG_VAR([GMOCK_CONFIG], [Path to gmock-config script])
|
||||
AC_ARG_VAR([GMOCK_CFLAGS], [Compiler flags for gmock])
|
||||
AC_ARG_VAR([GMOCK_LIBS], [Linker flags for gmock])
|
||||
AC_ARG_VAR([GTEST_CONFIG], [Path to gtest-config script])
|
||||
AC_ARG_VAR([GTEST_CFLAGS], [Compiler flags for gtest])
|
||||
AC_ARG_VAR([GTEST_LIBS], [Linker flags for gtest])
|
||||
if test x$system_test_libs = xtrue; then
|
||||
AC_CHECK_TOOL([GMOCK_CONFIG], [gmock-config])
|
||||
AC_CHECK_TOOL([GTEST_CONFIG], [gtest-config])
|
||||
GMOCK_CFLAGS=`$GMOCK_CONFIG --cppflags --cxxflags`
|
||||
GMOCK_LIBS=`$GMOCK_CONFIG --ldflags --libs`
|
||||
GTEST_CFLAGS=`$GTEST_CONFIG --cppflags --cxxflags`
|
||||
GTEST_LIBS=`$GTEST_CONFIG --ldflags --libs`
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(selftest,
|
||||
AS_HELP_STRING([--enable-selftest],
|
||||
@ -66,5 +195,10 @@ AC_ARG_ENABLE(selftest,
|
||||
[selftest=false])
|
||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CONFIG_FILES(m4_flatten([
|
||||
breakpad.pc
|
||||
breakpad-client.pc
|
||||
Makefile
|
||||
]))
|
||||
|
||||
AC_OUTPUT
|
||||
|
1
docs/OWNERS
Normal file
1
docs/OWNERS
Normal file
@ -0,0 +1 @@
|
||||
*
|
BIN
docs/breakpad.png
Normal file
BIN
docs/breakpad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
1023
docs/breakpad.svg
Normal file
1023
docs/breakpad.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 45 KiB |
224
docs/client_design.md
Normal file
224
docs/client_design.md
Normal file
@ -0,0 +1,224 @@
|
||||
# Breakpad Client Libraries
|
||||
|
||||
## Objective
|
||||
|
||||
The Breakpad client libraries are responsible for monitoring an application for
|
||||
crashes (exceptions), handling them when they occur by generating a dump, and
|
||||
providing a means to upload dumps to a crash reporting server. These tasks are
|
||||
divided between the “handler” (short for “exception handler”) library linked in
|
||||
to an application being monitored for crashes, and the “sender” library,
|
||||
intended to be linked in to a separate external program.
|
||||
|
||||
## Background
|
||||
|
||||
As one of the chief tasks of the client handler is to generate a dump, an
|
||||
understanding of [dump files](processor_design.md) will aid in understanding the
|
||||
handler.
|
||||
|
||||
## Overview
|
||||
|
||||
Breakpad provides client libraries for each of its target platforms. Currently,
|
||||
these exist for Windows on x86 and Mac OS X on both x86 and PowerPC. A Linux
|
||||
implementation has been written and is currently under review.
|
||||
|
||||
Because the mechanisms for catching exceptions and the methods for obtaining the
|
||||
information that a dump contains vary between operating systems, each target
|
||||
operating system requires a completely different handler implementation. Where
|
||||
multiple CPUs are supported for a single operating system, the handler
|
||||
implementation will likely also require separate code for each processor type to
|
||||
extract CPU-specific information. One of the goals of the Breakpad handler is to
|
||||
provide a prepackaged cross-platform system that masks many of these
|
||||
system-level differences and quirks from the application developer. Although the
|
||||
underlying implementations differ, the handler library for each system follows
|
||||
the same set of principles and exposes a similar interface.
|
||||
|
||||
Code that wishes to take advantage of Breakpad should be linked against the
|
||||
handler library, and should, at an appropriate time, install a Breakpad handler.
|
||||
For applications, it is generally desirable to install the handler as early in
|
||||
the start-up process as possible. Developers of library code using Breakpad to
|
||||
monitor itself may wish to install a Breakpad handler when the library is
|
||||
loaded, or may only want to install a handler when calls are made in to the
|
||||
library.
|
||||
|
||||
The handler can be triggered to generate a dump either by catching an exception
|
||||
or at the request of the application itself. The latter case may be useful in
|
||||
debugging assertions or other conditions where developers want to know how a
|
||||
program got in to a specific non-crash state. After generating a dump, the
|
||||
handler calls a user-specified callback function. The callback function may
|
||||
collect additional data about the program’s state, quit the program, launch a
|
||||
crash reporter application, or perform other tasks. Allowing for this
|
||||
functionality to be dictated by a callback function preserves flexibility.
|
||||
|
||||
The sender library is also has a separate implementation for each supported
|
||||
platform, because of the varying interfaces for accessing network resources on
|
||||
different operating systems. The sender transmits a dump along with other
|
||||
application-defined information to a crash report server via HTTP. Because dumps
|
||||
may contain sensitive data, the sender allows for the use of HTTPS.
|
||||
|
||||
The canonical example of the entire client system would be for a monitored
|
||||
application to link against the handler library, install a Breakpad handler from
|
||||
its main function, and provide a callback to launch a small crash reporter
|
||||
program. The crash reporter program would be linked against the sender library,
|
||||
and would send the crash dump when launched. A separate process is recommended
|
||||
for this function because of the unreliability inherent in doing any significant
|
||||
amount of work from a crashed process.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Exception Handler Installation
|
||||
|
||||
The mechanisms for installing an exception handler vary between operating
|
||||
systems. On Windows, it’s a relatively simple matter of making one call to
|
||||
register a [top-level exception filter]
|
||||
(http://msdn.microsoft.com/library/en-us/debug/base/setunhandledexceptionfilter.asp)
|
||||
callback function. On most Unix-like systems such as Linux, processes are
|
||||
informed of exceptions by the delivery of a signal, so an exception handler
|
||||
takes the form of a signal handler. The native mechanism to catch exceptions on
|
||||
Mac OS X requires a large amount of code to set up a Mach port, identify it as
|
||||
the exception port, and assign a thread to listen for an exception on that port.
|
||||
Just as the preparation of exception handlers differ, the manner in which they
|
||||
are called differs as well. On Windows and most Unix-like systems, the handler
|
||||
is called on the thread that caused the exception. On Mac OS X, the thread
|
||||
listening to the exception port is notified that an exception has occurred. The
|
||||
different implementations of the Breakpad handler libraries perform these tasks
|
||||
in the appropriate ways on each platform, while exposing a similar interface on
|
||||
each.
|
||||
|
||||
A Breakpad handler is embodied in an `ExceptionHandler` object. Because it’s a
|
||||
C++ object, `ExceptionHandler`s may be created as local variables, allowing them
|
||||
to be installed and removed as functions are called and return. This provides
|
||||
one possible way for a developer to monitor only a portion of an application for
|
||||
crashes.
|
||||
|
||||
### Exception Basics
|
||||
|
||||
Once an application encounters an exception, it is in an indeterminate and
|
||||
possibly hazardous state. Consequently, any code that runs after an exception
|
||||
occurs must take extreme care to avoid performing operations that might fail,
|
||||
hang, or cause additional exceptions. This task is not at all straightforward,
|
||||
and the Breakpad handler library seeks to do it properly, accounting for all of
|
||||
the minute details while allowing other application developers, even those with
|
||||
little systems programming experience, to reap the benefits. All of the Breakpad
|
||||
handler code that executes after an exception occurs has been written according
|
||||
to the following guidelines for safety at exception time:
|
||||
|
||||
* Use of the application heap is forbidden. The heap may be corrupt or
|
||||
otherwise unusable, and allocators may not function.
|
||||
* Resource allocation must be severely limited. The handler may create a new
|
||||
file to contain the dump, and it may attempt to launch a process to continue
|
||||
handling the crash.
|
||||
* Execution on the thread that caused the exception is significantly limited.
|
||||
The only code permitted to execute on this thread is the code necessary to
|
||||
transition handling to a dedicated preallocated handler thread, and the code
|
||||
to return from the exception handler.
|
||||
* Handlers shouldn’t handle crashes by attempting to walk stacks themselves,
|
||||
as stacks may be in inconsistent states. Dump generation should be performed
|
||||
by interfacing with the operating system’s memory manager and code module
|
||||
manager.
|
||||
* Library code, including runtime library code, must be avoided unless it
|
||||
provably meets the above guidelines. For example, this means that the STL
|
||||
string class may not be used, because it performs operations that attempt to
|
||||
allocate and use heap memory. It also means that many C runtime functions
|
||||
must be avoided, particularly on Windows, because of heap operations that
|
||||
they may perform.
|
||||
|
||||
A dedicated handler thread is used to preserve the state of the exception thread
|
||||
when an exception occurs: during dump generation, it is difficult if not
|
||||
impossible for a thread to accurately capture its own state. Performing all
|
||||
exception-handling functions on a separate thread is also critical when handling
|
||||
stack-limit-exceeded exceptions. It would be hazardous to run out of stack space
|
||||
while attempting to handle an exception. Because of the rule against allocating
|
||||
resources at exception time, the Breakpad handler library creates its handler
|
||||
thread when it installs its exception handler. On Mac OS X, this handler thread
|
||||
is created during the normal setup of the exception handler, and the handler
|
||||
thread will be signaled directly in the event of an exception. On Windows and
|
||||
Linux, the handler thread is signaled by a small amount of code that executes on
|
||||
the exception thread. Because the code that executes on the exception thread in
|
||||
this case is small and safe, this does not pose a problem. Even when an
|
||||
exception is caused by exceeding stack size limits, this code is sufficiently
|
||||
compact to execute entirely within the stack’s guard page without causing an
|
||||
exception.
|
||||
|
||||
The handler thread may also be triggered directly by a user call, even when no
|
||||
exception occurs, to allow dumps to be generated at any point deemed
|
||||
interesting.
|
||||
|
||||
### Filter Callback
|
||||
|
||||
When the handler thread begins handling an exception, it calls an optional
|
||||
user-defined filter callback function, which is responsible for judging whether
|
||||
Breakpad’s handler should continue handling the exception or not. This mechanism
|
||||
is provided for the benefit of library or plug-in code, whose developers may not
|
||||
be interested in reports of crashes that occur outside of their modules but
|
||||
within processes hosting their code. If the filter callback indicates that it is
|
||||
not interested in the exception, the Breakpad handler arranges for it to be
|
||||
delivered to any previously-installed handler.
|
||||
|
||||
### Dump Generation
|
||||
|
||||
Assuming that the filter callback approves (or does not exist), the handler
|
||||
writes a dump in a directory specified by the application developer when the
|
||||
handler was installed, using a previously generated unique identifier to avoid
|
||||
name collisions. The mechanics of dump generation also vary between platforms,
|
||||
but in general, the process involves enumerating each thread of execution, and
|
||||
capturing its state, including processor context and the active portion of its
|
||||
stack area. The dump also includes a list of the code modules loaded in to the
|
||||
application, and an indicator of which thread generated the exception or
|
||||
requested the dump. In order to avoid allocating memory during this process, the
|
||||
dump is written in place on disk.
|
||||
|
||||
### Post-Dump Behavior
|
||||
|
||||
Upon completion of writing the dump, a second callback function is called. This
|
||||
callback may be used to launch a separate crash reporting program or to collect
|
||||
additional data from the application. The callback may also be used to influence
|
||||
whether Breakpad will treat the exception as handled or unhandled. Even after a
|
||||
dump is successfully generated, Breakpad can be made to behave as though it
|
||||
didn’t actually handle an exception. This function may be useful for developers
|
||||
who want to test their applications with Breakpad enabled but still retain the
|
||||
ability to use traditional debugging techniques. It also allows a
|
||||
Breakpad-enabled application to coexist with a platform’s native crash reporting
|
||||
system, such as Mac OS X’ [CrashReporter]
|
||||
(http://developer.apple.com/technotes/tn2004/tn2123.html) and [Windows Error
|
||||
Reporting](http://msdn.microsoft.com/isv/resources/wer/).
|
||||
|
||||
Typically, when Breakpad handles an exception fully and no debuggers are
|
||||
involved, the crashed process will terminate.
|
||||
|
||||
Authors of both callback functions that execute within a Breakpad handler are
|
||||
cautioned that their code will be run at exception time, and that as a result,
|
||||
they should observe the same programming practices that the Breakpad handler
|
||||
itself adheres to. Notably, if a callback is to be used to collect additional
|
||||
data from an application, it should take care to read only “safe” data. This
|
||||
might involve accessing only static memory locations that are updated
|
||||
periodically during the course of normal program execution.
|
||||
|
||||
### Sender Library
|
||||
|
||||
The Breakpad sender library provides a single function to send a crash report to
|
||||
a crash server. It accepts a crash server’s URL, a map of key-value parameters
|
||||
that will accompany the dump, and the path to a dump file itself. Each of the
|
||||
key-value parameters and the dump file are sent as distinct parts of a multipart
|
||||
HTTP POST request to the specified URL using the platform’s native HTTP
|
||||
facilities. On Linux, [libcurl](http://curl.haxx.se/) is used for this function,
|
||||
as it is the closest thing to a standard HTTP library available on that
|
||||
platform.
|
||||
|
||||
## Future Plans
|
||||
|
||||
Although we’ve had great success with in-process dump generation by following
|
||||
our guidelines for safe code at exception time, we are exploring options for
|
||||
allowing dumps to be generated in a separate process, to further enhance the
|
||||
handler library’s robustness.
|
||||
|
||||
On Windows, we intend to offer tools to make it easier for Breakpad’s settings
|
||||
to be managed by the native group policy management system.
|
||||
|
||||
We also plan to offer tools that many developers would find desirable in the
|
||||
context of handling crashes, such as a mechanism to determine at launch if the
|
||||
program last terminated in a crash, and a way to calculate “crashiness” in terms
|
||||
of crashes over time or the number of application launches between crashes.
|
||||
|
||||
We are also investigating methods to capture crashes that occur early in an
|
||||
application’s launch sequence, including crashes that occur before a program’s
|
||||
main function begins executing.
|
35
docs/contributing_to_breakpad.md
Normal file
35
docs/contributing_to_breakpad.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Introduction
|
||||
|
||||
Thanks for thinking of contributing to Breakpad! Unfortunately there are some
|
||||
pesky legal issues to get out of the way, but they're quick and painless.
|
||||
|
||||
## Legal
|
||||
|
||||
If you're doing work individually, not as part of any employment, you'll need to
|
||||
sign the <a
|
||||
href='http://code.google.com/legal/individual-cla-v1.0.html'>Individual
|
||||
Contributor License Agreement</a>. This agreement can be completed
|
||||
electronically.
|
||||
|
||||
If you're contributing to Breakpad as part of your employment with another
|
||||
organization, you'll need to sign a <a
|
||||
href='http://code.google.com/legal/corporate-cla-v1.0.html'> Corporate
|
||||
Contributor License Agreement</a>. Once completed this document will need to be
|
||||
faxed.
|
||||
|
||||
**_IMPORTANT_**: The authors(you!) of the contributions will maintain all
|
||||
copyrights; the agreements you sign will grant rights to Google to use your
|
||||
work.
|
||||
|
||||
Thanks, and if you have any questions let me know and I'll loop in the legal guy
|
||||
here to get you an answer.
|
||||
|
||||
## Technical
|
||||
|
||||
Once you have signed the agreement you can be added to our contributors list and
|
||||
have write access to code. For full details on getting started see our trunk
|
||||
`README`.
|
||||
|
||||
## List of people who have signed contributor agreements
|
||||
|
||||
None so far.
|
128
docs/exception_handling.md
Normal file
128
docs/exception_handling.md
Normal file
@ -0,0 +1,128 @@
|
||||
The goal of this document is to give an overview of the exception handling
|
||||
options in breakpad.
|
||||
|
||||
# Basics
|
||||
|
||||
Exception handling is a mechanism designed to handle the occurrence of
|
||||
exceptions, special conditions that change the normal flow of program execution.
|
||||
|
||||
`SetUnhandledExceptionFilter` replaces all unhandled exceptions when Breakpad is
|
||||
enabled. TODO: More on first and second change and vectored v. try/catch.
|
||||
|
||||
There are two main types of exceptions across all platforms: in-process and
|
||||
out-of-process.
|
||||
|
||||
# In-Process
|
||||
|
||||
In process exception handling is relatively simple since the crashing process
|
||||
handles crash reporting. It is generally considered unsafe to write a minidump
|
||||
from a crashed process. For example, key data structures could be corrupted or
|
||||
the stack on which the exception handler runs could have been overwritten. For
|
||||
this reason all platforms also support some level of out-of-process exception
|
||||
handling.
|
||||
|
||||
## Windows
|
||||
|
||||
In-process exception handling Breakpad creates a 'handler head' that waits
|
||||
infinitely on a semaphore at start up. When this thread is woken it writes the
|
||||
minidump and signals to the excepting thread that it may continue. A filter will
|
||||
tell the OS to kill the process if the minidump is written successfully.
|
||||
Otherwise it continues.
|
||||
|
||||
# Out-of-Process
|
||||
|
||||
Out-of-process exception handling is more complicated than in-process exception
|
||||
handling because of the need to set up a separate process that can read the
|
||||
state of the crashing process.
|
||||
|
||||
## Windows
|
||||
|
||||
Breakpad uses two abstractions around the exception handler to make things work:
|
||||
`CrashGenerationServer` and `CrashGenerationClient`. The constructor for these
|
||||
takes a named pipe name.
|
||||
|
||||
During server start up a named pipe and registers callbacks for client
|
||||
connections are created. The named pipe is used for registration and all IO on
|
||||
the pipe is done asynchronously. `OnPipeConnected` is called when a client
|
||||
attempts to connect (call `CreateFile` on the pipe). `OnPipeConnected` does the
|
||||
state machine transition from `Initial` to `Connecting` and on through
|
||||
`Reading`, `Reading_Done`, `Writing`, `Writing_Done`, `Reading_ACK`, and
|
||||
`Disconnecting`.
|
||||
|
||||
When registering callbacks, the client passes in two pointers to pointers: 1. A
|
||||
pointer to the `EXCEPTION_INFO` pointer 1. A pointer to the `MDRawAssertionInfo`
|
||||
which handles various non-exception failures like assertions
|
||||
|
||||
The essence of registration is adding a "`ClientInfo`" object that contains
|
||||
handles used for synchronization with the crashing process to an array
|
||||
maintained by the server. This is how we can keep track of all the clients on
|
||||
the system that have registered for minidumps. These handles are: *
|
||||
`server_died(mutex)` * `dump_requested(Event)` * `dump_generated(Event)`
|
||||
|
||||
The server registers asynchronous waits on these events with the `ClientInfo`
|
||||
object as the callback context. When the `dump_requested` event is set by the
|
||||
client, the `OnDumpRequested()` callback is called. The server uses the handles
|
||||
inside `ClientInfo` to communicate with the child process. Once the child sets
|
||||
the event, it waits for two objects: 1. the `dump_generated` event 1. the
|
||||
`server_died` mutex
|
||||
|
||||
In the end handles are "duped" into the client process, and the clients use
|
||||
`SetEvent` to request events, wait on the other event, or the `server_died`
|
||||
mutex.
|
||||
|
||||
## Linux
|
||||
|
||||
### Current Status
|
||||
|
||||
As of July 2011, Linux had a minidump generator that is not entirely
|
||||
out-of-process. The minidump was generated from a separate process, but one that
|
||||
shared an address space, file descriptors, signal handles and much else with the
|
||||
crashing process. It worked by using the `clone()` system call to duplicate the
|
||||
crashing process, and then uses `ptrace()` and the `/proc` file system to
|
||||
retrieve the information required to write the minidump. Since then Breakpad has
|
||||
updated Linux exception handling to provide more benefits of out-of-process
|
||||
report generation.
|
||||
|
||||
### Proposed Design
|
||||
|
||||
#### Overview
|
||||
|
||||
Breakpad would use a per-user daemon to write out a minidump that does not have,
|
||||
interact with or depend on the crashing process. We don't want to start a new
|
||||
separate process every time a user launches a Breakpad-enabled process. Doing
|
||||
one daemon per machine is unacceptable for security concerns around one user
|
||||
being able to initiate a minidump generation for another user's process.
|
||||
|
||||
#### Client/Server Communication
|
||||
|
||||
On Breakpad initialization in a process, the initializer would check if the
|
||||
daemon is running and, if not, start it. The race condition between the check
|
||||
and the initialization is not a problem because multiple daemons can check if
|
||||
the IPC endpoint already exists and if a server is listening. Even if multiple
|
||||
copies of the daemon try to `bind()` the filesystem to name the socket, all but
|
||||
one will fail and can terminate.
|
||||
|
||||
This point is relevant for error handling conditions. Linux does not clean the
|
||||
file system representation of a UNIX domain socket even if both endpoints
|
||||
terminate, so checking for existence is not strong enough. However checking the
|
||||
process list or sending a ping on the socket can handle this.
|
||||
|
||||
Breakpad uses UNIX domain sockets since they support full duplex communication
|
||||
(unlike Windows, named pipes on Linux are half) and the kernal automatically
|
||||
creates a private channel between the client and server once the client calls
|
||||
`connect()`.
|
||||
|
||||
#### Minidump Generation
|
||||
|
||||
Breakpad could use the current system with `ptrace()` and `/proc` within the
|
||||
daemon executable.
|
||||
|
||||
Overall the operations look like: 1. Signal from OS indicating crash 1. Signal
|
||||
Handler suspends all threads except itself 1. Signal Handler sends
|
||||
`CRASH_DUMP_REQUEST` message to server and waits for response 1. Server inspects
|
||||
1. Minidump is asynchronously written to disk by the server 1. Server responds
|
||||
indicating inspection is done
|
||||
|
||||
## Mac OSX
|
||||
|
||||
Out-of-process exception handling is fully supported on Mac.
|
121
docs/getting_started_with_breakpad.md
Normal file
121
docs/getting_started_with_breakpad.md
Normal file
@ -0,0 +1,121 @@
|
||||
# Introduction
|
||||
|
||||
Breakpad is a library and tool suite that allows you to distribute an
|
||||
application to users with compiler-provided debugging information removed,
|
||||
record crashes in compact "minidump" files, send them back to your server, and
|
||||
produce C and C++ stack traces from these minidumps. Breakpad can also write
|
||||
minidumps on request for programs that have not crashed.
|
||||
|
||||
Breakpad is currently used by Google Chrome, Firefox, Google Picasa, Camino,
|
||||
Google Earth, and other projects.
|
||||
|
||||
![http://google-breakpad.googlecode.com/svn/wiki/breakpad.png]
|
||||
(http://google-breakpad.googlecode.com/svn/wiki/breakpad.png)
|
||||
|
||||
Breakpad has three main components:
|
||||
|
||||
* The **client** is a library that you include in your application. It can
|
||||
write minidump files capturing the current threads' state and the identities
|
||||
of the currently loaded executable and shared libraries. You can configure
|
||||
the client to write a minidump when a crash occurs, or when explicitly
|
||||
requested.
|
||||
|
||||
* The **symbol dumper** is a program that reads the debugging information
|
||||
produced by the compiler and produces a **symbol file**, in [Breakpad's own
|
||||
format](symbol_files.md).
|
||||
|
||||
* The **processor** is a program that reads a minidump file, finds the
|
||||
appropriate symbol files for the versions of the executables and shared
|
||||
libraries the minidump mentions, and produces a human-readable C/C++ stack
|
||||
trace.
|
||||
|
||||
# The minidump file format
|
||||
|
||||
The minidump file format is similar to core files but was developed by Microsoft
|
||||
for its crash-uploading facility. A minidump file contains:
|
||||
|
||||
* A list of the executable and shared libraries that were loaded in the
|
||||
process at the time the dump was created. This list includes both file names
|
||||
and identifiers for the particular versions of those files that were loaded.
|
||||
|
||||
* A list of threads present in the process. For each thread, the minidump
|
||||
includes the state of the processor registers, and the contents of the
|
||||
threads' stack memory. These data are uninterpreted byte streams, as the
|
||||
Breakpad client generally has no debugging information available to produce
|
||||
function names or line numbers, or even identify stack frame boundaries.
|
||||
|
||||
* Other information about the system on which the dump was collected:
|
||||
processor and operating system versions, the reason for the dump, and so on.
|
||||
|
||||
Breakpad uses Windows minidump files on all platforms, instead of the
|
||||
traditional core files, for several reasons:
|
||||
|
||||
* Core files can be very large, making them impractical to send across a
|
||||
network to the collector for processing. Minidumps are smaller, as they were
|
||||
designed to be used this way.
|
||||
|
||||
* The core file format is poorly documented. For example, the Linux Standards
|
||||
Base does not describe how registers are stored in `PT_NOTE` segments.
|
||||
|
||||
* It is harder to persuade a Windows machine to produce a core dump file than
|
||||
it is to persuade other machines to write a minidump file.
|
||||
|
||||
* It simplifies the Breakpad processor to support only one file format.
|
||||
|
||||
# Overview/Life of a minidump
|
||||
|
||||
A minidump is generated via calls into the Breakpad library. By default,
|
||||
initializing Breakpad installs an exception/signal handler that writes a
|
||||
minidump to disk at exception time. On Windows, this is done via
|
||||
`SetUnhandledExceptionFilter()`; on OS X, this is done by creating a thread that
|
||||
waits on the Mach exception port; and on Linux, this is done by installing a
|
||||
signal handler for various exceptions like `SIGILL, SIGSEGV` etc.
|
||||
|
||||
Once the minidump is generated, each platform has a slightly different way of
|
||||
uploading the crash dump. On Windows & Linux, a separate library of functions is
|
||||
provided that can be called into to do the upload. On OS X, a separate process
|
||||
is spawned that prompts the user for permission, if configured to do so, and
|
||||
sends the file.
|
||||
|
||||
# Terminology
|
||||
|
||||
**In-process vs. out-of-process exception handling** - it's generally considered
|
||||
that writing the minidump from within the crashed process is unsafe - key
|
||||
process data structures could be corrupted, or the stack on which the exception
|
||||
handler runs could have been overwritten, etc. All 3 platforms support what's
|
||||
known as "out-of-process" exception handling.
|
||||
|
||||
# Integration overview
|
||||
|
||||
## Breakpad Code Overview
|
||||
|
||||
All the client-side code is found by visiting the Google Project at
|
||||
http://code.google.com/p/google-breakpad. The following directory structure is
|
||||
present in the `src` directory:
|
||||
|
||||
* `processor` Contains minidump-processing code that is used on the server
|
||||
side and isn't of use on the client side
|
||||
* `client` Contains client minidump-generation libraries for all platforms
|
||||
* `tools` Contains source code & projects for building various tools on each
|
||||
platform.
|
||||
|
||||
(Among other directories)
|
||||
|
||||
* <a
|
||||
href='http://code.google.com/p/google-breakpad/wiki/WindowsClientIntegration'>Windows
|
||||
Integration Guide</a>
|
||||
* <a
|
||||
href='http://code.google.com/p/google-breakpad/wiki/MacBreakpadStarterGuide'>Mac
|
||||
Integration Guide</a>
|
||||
* <a href='http://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide'>
|
||||
Linux Integration Guide</a>
|
||||
|
||||
## Build process specifics(symbol generation)
|
||||
|
||||
This applies to all platforms. Inside `src/tools/{platform}/dump_syms` is a tool
|
||||
that can read debugging information for each platform (e.g. for OS X/Linux,
|
||||
DWARF and STABS, and for Windows, PDB files) and generate a Breakpad symbol
|
||||
file. This tool should be run on your binary before it's stripped(in the case of
|
||||
OS X/Linux) and the symbol files need to be stored somewhere that the minidump
|
||||
processor can find. There is another tool, `symupload`, that can be used to
|
||||
upload symbol files if you have written a server that can accept them.
|
110
docs/linux_starter_guide.md
Normal file
110
docs/linux_starter_guide.md
Normal file
@ -0,0 +1,110 @@
|
||||
# How To Add Breakpad To Your Linux Application
|
||||
|
||||
This document is an overview of using the Breakpad client libraries on Linux.
|
||||
|
||||
## Building the Breakpad libraries
|
||||
|
||||
Breakpad provides an Autotools build system that will build both the Linux
|
||||
client libraries and the processor libraries. Running `./configure && make` in
|
||||
the Breakpad source directory will produce
|
||||
**src/client/linux/libbreakpad\_client.a**, which contains all the code
|
||||
necessary to produce minidumps from an application.
|
||||
|
||||
## Integrating Breakpad into your Application
|
||||
|
||||
First, configure your build process to link **libbreakpad\_client.a** into your
|
||||
binary, and set your include paths to include the **src** directory in the
|
||||
**google-breakpad** source tree. Next, include the exception handler header:
|
||||
|
||||
```cpp
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
```
|
||||
|
||||
Now you can instantiate an `ExceptionHandler` object. Exception handling is active for the lifetime of the `ExceptionHandler` object, so you should instantiate it as early as possible in your application's startup process, and keep it alive for as close to shutdown as possible. To do anything useful, the `ExceptionHandler` constructor requires a path where it can write minidumps, as well as a callback function to receive information about minidumps that were written:
|
||||
|
||||
```cpp
|
||||
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
|
||||
void* context, bool succeeded) {
|
||||
printf("Dump path: %s\n", descriptor.path());
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void crash() { volatile int* a = (int*)(NULL); *a = 1; }
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
google_breakpad::MinidumpDescriptor descriptor("/tmp");
|
||||
google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1);
|
||||
crash();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Compiling and running this example should produce a minidump file in /tmp, and
|
||||
it should print the minidump filename before exiting. You can read more about
|
||||
the other parameters to the `ExceptionHandler` constructor [in the exception_handler.h source file][1].
|
||||
|
||||
[1]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/client/linux/handler/exception_handler.h
|
||||
|
||||
**Note**: You should do as little work as possible in the callback function.
|
||||
Your application is in an unsafe state. It may not be safe to allocate memory or
|
||||
call functions from other shared libraries. The safest thing to do is `fork` and
|
||||
`exec` a new process to do any work you need to do. If you must do some work in
|
||||
the callback, the Breakpad source contains [some simple reimplementations of libc functions][2], to avoid calling directly into
|
||||
libc, as well as [a header file for making Linux system calls][3] (in **src/third\_party/lss**) to avoid calling into other shared libraries.
|
||||
|
||||
[2]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/common/linux/linux_libc_support.h
|
||||
[3]: https://chromium.googlesource.com/linux-syscall-support/+/master
|
||||
|
||||
## Sending the minidump file
|
||||
|
||||
In a real application, you would want to handle the minidump in some way, likely
|
||||
by sending it to a server for analysis. The Breakpad source tree contains [some
|
||||
HTTP upload source][4] that you might find useful, as well as [a minidump upload tool][5].
|
||||
|
||||
[4]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/common/linux/http_upload.h
|
||||
[5]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/tools/linux/symupload/minidump_upload.cc
|
||||
|
||||
## Producing symbols for your application
|
||||
|
||||
To produce useful stack traces, Breakpad requires you to convert the debugging
|
||||
symbols in your binaries to [text-format symbol files][6]. First, ensure that you've compiled your binaries with `-g` to
|
||||
include debugging symbols. Next, compile the `dump_syms` tool by running
|
||||
`configure && make` in the Breakpad source directory. Next, run `dump_syms` on
|
||||
your binaries to produce the text-format symbols. For example, if your main
|
||||
binary was named `test`:
|
||||
|
||||
[6]: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
|
||||
|
||||
```
|
||||
$ google-breakpad/src/tools/linux/dump_syms/dump_syms ./test > test.sym
|
||||
```
|
||||
|
||||
In order to use these symbols with the `minidump_stackwalk` tool, you will need
|
||||
to place them in a specific directory structure. The first line of the symbol
|
||||
file contains the information you need to produce this directory structure, for
|
||||
example (your output will vary):
|
||||
|
||||
```
|
||||
$ head -n1 test.sym MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test
|
||||
$ mkdir -p ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
|
||||
$ mv test.sym ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
|
||||
```
|
||||
|
||||
You may also find the [symbolstore.py][7] script in the Mozilla repository useful, as it encapsulates these steps.
|
||||
|
||||
[7]: https://dxr.mozilla.org/mozilla-central/source/toolkit/crashreporter/tools/symbolstore.py
|
||||
|
||||
## Processing the minidump to produce a stack trace
|
||||
|
||||
Breakpad includes a tool called `minidump_stackwalk` which can take a minidump
|
||||
plus its corresponding text-format symbols and produce a symbolized stacktrace.
|
||||
It should be in the **google-breakpad/src/processor** directory if you compiled
|
||||
the Breakpad source using the directions above. Simply pass it the minidump and
|
||||
the symbol path as commandline parameters:
|
||||
|
||||
```
|
||||
$ google-breakpad/src/processor/minidump_stackwalk minidump.dmp ./symbols
|
||||
```
|
||||
|
||||
It produces verbose output on stderr, and the stacktrace on stdout, so you may
|
||||
want to redirect stderr.
|
47
docs/linux_system_calls.md
Normal file
47
docs/linux_system_calls.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Introduction
|
||||
|
||||
Linux implements its userland-to-kernel transition using a special library
|
||||
called linux-gate.so that is mapped by the kernel into every process. For more
|
||||
information, see
|
||||
|
||||
http://www.trilithium.com/johan/2005/08/linux-gate/
|
||||
|
||||
In a nutshell, the problem is that the system call gate function,
|
||||
kernel\_vsyscall does not use EBP to point to the frame pointer.
|
||||
|
||||
However, the Breakpad processor supports special frames like this via STACK
|
||||
lines in the symbol file. If you look in src/client/linux/data you will see
|
||||
symbol files for linux-gate.so for both Intel & AMD(the implementation of
|
||||
kernel\_vsyscall changes depending on the CPU manufacturer). When processing
|
||||
minidumps from Linux 2.6, having these symbol files is necessary for walking the
|
||||
stack for crashes that happen while a thread is in a system call.
|
||||
|
||||
If you're just interested in processing minidumps, those two symbol files should
|
||||
be all you need!
|
||||
|
||||
# Details
|
||||
|
||||
The particular details of understanding the linux-gate.so symbol files can be
|
||||
found by reading about STACK lines inside
|
||||
src/common/windows/pdb\_source\_line\_writer.cc, and the above link. To
|
||||
summarize briefly, we just have to inform the processor how to get to the
|
||||
previous frame when the EIP is inside kernel\_vsyscall, and we do that by
|
||||
telling the processor how many bytes kernel\_vsyscall has pushed onto the stack
|
||||
in it's prologue. For example, one of the symbol files looks somewhat like the
|
||||
following:
|
||||
|
||||
MODULE Linux x86 random\_debug\_id linux-gate.so PUBLIC 400 0 kernel\_vsyscall
|
||||
STACK WIN 4 100 1 1 0 0 0 0 0 1
|
||||
|
||||
The PUBLIC line indicates that kernel\_vsyscall is at offset 400 (in bytes) from
|
||||
the beginning of linux-gate.so. The STACK line indicates the size of the
|
||||
function(100), how many bytes it pushes(1), and how many bytes it pops(1). The
|
||||
last 1 indicates that EBP is pushed onto the stack before being used by the
|
||||
function.
|
||||
|
||||
# Warnings
|
||||
|
||||
These functions might change significantly depending on kernel version. In my
|
||||
opinion, the actual function stack information is unlikely to change frequently,
|
||||
but the Linux kernel might change the address of kernel\_vsyscall w.r.t the
|
||||
beginning of linux-gate.so, which would cause these symbol files to be invalid.
|
184
docs/mac_breakpad_starter_guide.md
Normal file
184
docs/mac_breakpad_starter_guide.md
Normal file
@ -0,0 +1,184 @@
|
||||
# How To Add Breakpad To Your Mac Client Application
|
||||
|
||||
This document is a step-by-step recipe to get your Mac client app to build with
|
||||
Breakpad.
|
||||
|
||||
## Preparing a binary build of Breakpad for use in your tree
|
||||
|
||||
You can either check in a binary build of the Breakpad framework & tools or
|
||||
build it as a dependency of your project. The former is recommended, and
|
||||
detailed here, since building dependencies through other projects is
|
||||
problematic(matching up configuration names), and the Breakpad code doesn't
|
||||
change nearly often enough as your application's will.
|
||||
|
||||
## Building the requisite targets
|
||||
|
||||
All directories are relative to the `src` directory of the Breakpad checkout.
|
||||
|
||||
* Build the 'All' target of `client/mac/Breakpad.xcodeproj` in Release mode.
|
||||
* Execute `cp -R client/mac/build/Release/Breakpad.framework <location in your
|
||||
source tree>`
|
||||
* Inside `tools/mac/dump_syms` directory, build dump\_syms.xcodeproj, and copy
|
||||
tools/mac/dump\_syms/build/Release/dump\_syms to a safe location where it
|
||||
can be run during the build process.
|
||||
|
||||
## Adding Breakpad.framework
|
||||
|
||||
Inside your application's framework, add the Breakpad.Framework to your
|
||||
project's framework settings. When you select it from the file chooser, it will
|
||||
let you pick a target to add it to; go ahead and check the one that's relevant
|
||||
to your application.
|
||||
|
||||
## Copy Breakpad into your Application Package
|
||||
|
||||
Copy Breakpad into your Application Package, so it will be around at run time.
|
||||
|
||||
Go to the Targets section of your Xcode Project window. Hit the disclosure
|
||||
triangle to reveal the build phases of your application. Add a new Copy Files
|
||||
phase using the Contextual menu (Control Click). On the General panel of the new
|
||||
'Get Info' of this new phase, set the destination to 'Frameworks' Close the
|
||||
'Info' panel. Use the Contextual Menu to Rename your new phase 'Copy Frameworks'
|
||||
Now drag Breakpad again into this Copy Frameworks phase. Drag it from whereever
|
||||
it appears in the project file tree.
|
||||
|
||||
## Add a New Run Script build phase
|
||||
|
||||
Near the end of the build phases, add a new Run Script build phase. This will be
|
||||
run before Xcode calls /usr/bin/strip on your project. This is where you'll be
|
||||
calling dump\_sym to output the symbols for each architecture of your build. In
|
||||
my case, the relevant lines read:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
$TOOL_DIR=<location of dump_syms from step 3 above>
|
||||
|
||||
"$TOOL_DIR/dump_syms" -a ppc "$PROD" > "$TARGET_NAME ppc.breakpad"
|
||||
|
||||
"$TOOL_DIR/dump_syms" -a i386 "$PROD" > "$TARGET_NAME i386.breakpad"
|
||||
```
|
||||
|
||||
## Adjust the Project Settings
|
||||
|
||||
* Turn on Separate Strip,
|
||||
* Set the Strip Style to Non-Global Symbols.
|
||||
|
||||
## Write Code!
|
||||
|
||||
You'll need to have an object that acts as the delegate for NSApplication.
|
||||
Inside this object's header, you'll need to add
|
||||
|
||||
1. add an ivar for Breakpad and
|
||||
2. a declaration for the applicationShouldTerminate:(NSApplication`*` sender)
|
||||
message.
|
||||
|
||||
```
|
||||
#import <Breakpad/Breakpad.h>
|
||||
|
||||
@interface BreakpadTest : NSObject {
|
||||
.
|
||||
.
|
||||
.
|
||||
BreakpadRef breakpad;
|
||||
.
|
||||
.
|
||||
.
|
||||
}
|
||||
.
|
||||
.
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
||||
.
|
||||
.
|
||||
@end
|
||||
```
|
||||
|
||||
Inside your object's implementation file,
|
||||
|
||||
1. add the following method InitBreakpad
|
||||
2. modify your awakeFromNib method to look like the one below,
|
||||
3. modify/add your application's delegate method to look like the one below
|
||||
|
||||
```
|
||||
static BreakpadRef InitBreakpad(void) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
BreakpadRef breakpad = 0;
|
||||
NSDictionary *plist = [[NSBundle mainBundle] infoDictionary];
|
||||
if (plist) {
|
||||
// Note: version 1.0.0.4 of the framework changed the type of the argument
|
||||
// from CFDictionaryRef to NSDictionary * on the next line:
|
||||
breakpad = BreakpadCreate(plist);
|
||||
}
|
||||
[pool release];
|
||||
return breakpad;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
breakpad = InitBreakpad();
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
BreakpadRelease(breakpad);
|
||||
return NSTerminateNow;
|
||||
}
|
||||
```
|
||||
|
||||
## Configure Breakpad
|
||||
|
||||
Configure Breakpad for your application.
|
||||
|
||||
1. Take a look inside the Breakpad.framework at the Breakpad.h file for the
|
||||
keys, default values, and descriptions to be passed to BreakpadCreate().
|
||||
2. Add/Edit the Breakpad specific entries in the dictionary passed to
|
||||
BreakpadCreate() -- typically your application's info plist.
|
||||
|
||||
Example from the Notifier Info.plist:
|
||||
`<key>BreakpadProduct</key><string>Google_Notifier_Mac</string>
|
||||
<key>BreakpadProductDisplay</key><string>${PRODUCT_NAME}</string>
|
||||
`
|
||||
|
||||
## Build Your Application
|
||||
|
||||
Almost done!
|
||||
|
||||
## Verify
|
||||
|
||||
Double-check:
|
||||
|
||||
Your app should have in its package contents:
|
||||
myApp.app/Contents/Frameworks/Breakpad.framework.
|
||||
|
||||
The symbol files have reasonable contents (you can look at them with a text
|
||||
editor.)
|
||||
|
||||
Look again at the Copy Frameworks phase of your project. Are you leaking .h
|
||||
files? Select them and delete them. (If you drag a bunch of files into your
|
||||
project, Xcode often wants to copy your .h files into the build, revealing
|
||||
Google secrets. Be vigilant!)
|
||||
|
||||
## Upload the symbol file
|
||||
|
||||
You'll need to configure your build process to store symbols in a location that
|
||||
is accessible by the minidump processor. There is a tool in tools/mac/symupload
|
||||
that can be used to send the symbol file via HTTP post.
|
||||
|
||||
1. Test
|
||||
|
||||
Configure breakpad to send reports to a URL by adding to your app's Info.plist:
|
||||
|
||||
```
|
||||
<key>BreakpadURL</key>
|
||||
<string>upload URL</string>
|
||||
<key>BreakpadReportInterval</key>
|
||||
<string>30</string>
|
||||
```
|
||||
|
||||
## Final Notes
|
||||
|
||||
Breakpad checks whether it is being run under a debugger, and if so, normally
|
||||
does nothing. But, you can force Breakpad to function under a debugger by
|
||||
setting the Unix shell variable BREAKPAD\_IGNORE\_DEBUGGER to a non-zero value.
|
||||
You can bracket the source code in the above Write The Code step with #if DEBUG
|
||||
to completely eliminate it from Debug builds. See
|
||||
//depot/googlemac/GoogleNotifier/main.m for an example. FYI, when your process
|
||||
forks(), exception handlers are reset to the default for child processes. So
|
||||
they must reinitialize Breakpad, otherwise exceptions will be handled by Apple's
|
||||
Crash Reporter.
|
84
docs/mozilla_brown_bag_talk.md
Normal file
84
docs/mozilla_brown_bag_talk.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Breakpad Crash Reporting for Mozilla
|
||||
|
||||
* January 24, 2007
|
||||
* Links updated February 14, 2007
|
||||
* Mozilla HQ
|
||||
* Mark Mentovai
|
||||
* Brian Ryner
|
||||
|
||||
## What is a crash reporter?
|
||||
|
||||
* Enables developers to analyze crashes that occur in the wild
|
||||
* Produces stack backtraces that help identify how a program failed
|
||||
* Offers higher-level data aggregation (topcrashes, MTBF statistics)
|
||||
|
||||
## Motivation
|
||||
|
||||
* Talkback is proprietary and unmaintained
|
||||
* Smaller open-source projects have few options
|
||||
* Larger projects need flexibility and scalability
|
||||
|
||||
## Design Options
|
||||
|
||||
* Stackwalking done on client
|
||||
* Apple CrashReporter
|
||||
* GNOME BugBuddy
|
||||
* Client sends memory dump
|
||||
* Talkback
|
||||
* Windows Error Reporting
|
||||
* Breakpad
|
||||
|
||||
## Goals
|
||||
|
||||
* Provide libraries around which systems can be based
|
||||
* Open-source
|
||||
* Cross-platform
|
||||
* Mac OS X x86, PowerPC
|
||||
* Linux x86
|
||||
* Windows x86
|
||||
* No requirement to distribute symbols
|
||||
|
||||
## Client Libraries
|
||||
|
||||
* Exception handler installed at application startup
|
||||
* Spawns a separate thread
|
||||
* Minidump file written at crash time
|
||||
* Format used by Windows debuggers
|
||||
* Separate application invoked to send
|
||||
* HTTP[S](S.md) POST, can include additional parameters
|
||||
|
||||
## Symbols
|
||||
|
||||
* Cross-platform symbol file format
|
||||
* Contents
|
||||
* Function names
|
||||
* Source file names and line numbers
|
||||
* Windows: Frame pointer omission data
|
||||
* Future: parameters and local variables
|
||||
* Symbol conversion methods
|
||||
|
||||
## Processor
|
||||
|
||||
* Examines minidump file and invokes stackwalker
|
||||
* Symbol files requested from a SymbolSupplier
|
||||
* Produces stack trace
|
||||
* Output may be placed where convenient
|
||||
|
||||
## Intergation
|
||||
|
||||
* Breakpad client present in Gran Paradiso Alpha 1 for Windows
|
||||
* Disabled by default
|
||||
* Enable with `MOZ_AIRBAG`
|
||||
* Proof-of-concept collector
|
||||
* http://mavra.perilith.com/~luser/airbag-collector/list.pl
|
||||
* Other platforms coming soon
|
||||
|
||||
## More Information
|
||||
|
||||
* Project home: http://code.google.com/p/google-breakpad/
|
||||
* Mailing lists
|
||||
* [google-breakpad-dev@googlegroups.com]
|
||||
(http://groups.google.com/group/google-breakpad-dev/)
|
||||
* [google-breakpad-discuss@googlegroups.com]
|
||||
(http://groups.google.com/group/google-breakpad-discuss/)
|
||||
* Ask me (irc.mozilla.org: mento)
|
230
docs/processor_design.md
Normal file
230
docs/processor_design.md
Normal file
@ -0,0 +1,230 @@
|
||||
# Breakpad Processor Library
|
||||
|
||||
## Objective
|
||||
|
||||
The Breakpad processor library is an open-source framework to access the the
|
||||
information contained within crash dumps for multiple platforms, and to use that
|
||||
information to produce stack traces showing the call chain of each thread in a
|
||||
process. After processing, this data is made available to users of the library.
|
||||
|
||||
## Background
|
||||
|
||||
The Breakpad processor is intended to sit at the core of a comprehensive
|
||||
crash-reporting system that does not require debugging information to be
|
||||
provided to those running applications being monitored. Some existing
|
||||
crash-reporting systems, such as [GNOME](http://www.gnome.org/)’s Bug-Buddy and
|
||||
[Apple](http://www.apple.com/)’s [CrashReporter]
|
||||
(http://developer.apple.com/technotes/tn2004/tn2123.html), require symbolic
|
||||
information to be present on the end user’s computer; in the case of
|
||||
CrashReporter, the reports are transmitted only to Apple, not to third-party
|
||||
developers. Other systems, such as [Microsoft](http://www.microsoft.com/)’s
|
||||
[Windows Error Reporting](http://msdn.microsoft.com/isv/resources/wer/) and
|
||||
SupportSoft’s Talkback, transmit only a snapshot of a crashed process’ state,
|
||||
which can later be combined with symbolic debugging information without the need
|
||||
for it to be present on end users’ computers. Because symbolic debugging
|
||||
information consumes a large amount of space and is otherwise not needed during
|
||||
the normal operation of software, and because some developers are reluctant to
|
||||
release debugging symbols to their customers, Breakpad follows the latter
|
||||
approach.
|
||||
|
||||
We know of no currently-maintained crash-reporting systems that meet our
|
||||
requirements, which are to: * allow for symbols to be separate from the
|
||||
application, * handle crash reports from multiple platforms, * allow developers
|
||||
to operate their own crash-reporting platform, and to * be open-source. Windows
|
||||
Error Reporting only functions for Microsoft products, and requires the
|
||||
involvement of Microsoft’s servers. Talkback, while cross-platform, has not been
|
||||
maintained and at this point does not support Mac OS X on x86, which we consider
|
||||
to be a significant platform. Talkback is also closed-source commercial
|
||||
software, and has very specific requirements for its server platform.
|
||||
|
||||
We are aware of Windows-only crash-reporting systems that leverage Microsoft’s
|
||||
debugging interfaces. Such systems, even if extended to support dumps from other
|
||||
platforms, are tied to using Windows for at least a portion of the processor
|
||||
platform.
|
||||
|
||||
## Overview
|
||||
|
||||
The Breakpad processor itself is written in standard C++ and will work on a
|
||||
variety of platforms. The dumps it accepts may also have been created on a
|
||||
variety of systems. The library is able to combine dumps with symbolic debugging
|
||||
information to create stack traces that include function signatures. The
|
||||
processor library includes simple command-line tools to examine dumps and
|
||||
process them, producing stack traces. It also exposes several layers of APIs
|
||||
enabling crash-reporting systems to be built around the Breakpad processor.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Dump Files
|
||||
|
||||
In the processor, the dump data is of primary significance. Dumps typically
|
||||
contain:
|
||||
|
||||
* CPU context (register data) as it was at the time the crash occurred, and an
|
||||
indication of which thread caused the crash. General-purpose registers are
|
||||
included, as are special-purpose registers such as the instruction pointer
|
||||
(program counter).
|
||||
* Information about each thread of execution within a crashed process,
|
||||
including:
|
||||
* The memory region used for each thread’s stack.
|
||||
* CPU context for each thread, which for various reasons is not the same
|
||||
as the crash context in the case of the crashed thread.
|
||||
* A list of loaded code segments (or modules), including:
|
||||
* The name of the file (`.so`, `.exe`, `.dll`, etc.) which provides the
|
||||
code.
|
||||
* The boundaries of the memory region in which the code segment is visible
|
||||
to the process.
|
||||
* A reference to the debugging information for the code module, when such
|
||||
information is available.
|
||||
|
||||
Ordinarily, dumps are produced as a result of a crash, but other triggers may be
|
||||
set to produce dumps at any time a developer deems appropriate. The Breakpad
|
||||
processor can handle dumps in the minidump format, either generated by an
|
||||
[Breakpad client “handler”](client_design.md) implementation, or by another
|
||||
implementation that produces dumps in this format. The
|
||||
[DbgHelp.dll!MiniDumpWriteDump]
|
||||
(http://msdn2.microsoft.com/en-us/library/ms680360.aspx) function on Windows
|
||||
produces dumps in this format, and is the basis for the Breakpad handler
|
||||
implementation on that platform.
|
||||
|
||||
The [minidump format]
|
||||
(http://msdn.microsoft.com/en-us/library/ms679293%28VS.85%29.aspx) is
|
||||
essentially a simple container format, organized as a series of streams. Each
|
||||
stream contains some type of data relevant to the crash. A typical “normal”
|
||||
minidump contains streams for the thread list, the module list, the CPU context
|
||||
at the time of the crash, and various bits of additional system information.
|
||||
Other types of minidump can be generated, such as a full-memory minidump, which
|
||||
in addition to stack memory contains snapshots of all of a process’ mapped
|
||||
memory regions.
|
||||
|
||||
The minidump format was chosen as Breakpad’s dump format because it has an
|
||||
established track record on Windows, and it can be adapted to meet the needs of
|
||||
the other platforms that Breakpad supports. Most other operating systems use
|
||||
“core” files as their native dump formats, but the capabilities of core files
|
||||
vary across platforms, and because core files are usually presented in a
|
||||
platform’s native executable format, there are complications involved in
|
||||
accessing the data contained therein without the benefit of the header files
|
||||
that define an executable format’s entire structure. Because minidumps are
|
||||
leaner than a typical executable format, a redefinition of the format in a
|
||||
cross-platform header file, `minidump_format.h`, was a straightforward task.
|
||||
Similarly, the capabilities of the minidump format are understood, and because
|
||||
it provides an extensible container, any of Breakpad’s needs that could not be
|
||||
met directly by the standard minidump format could likely be met by extending it
|
||||
as needed. Finally, using this format means that the dump file is compatible
|
||||
with native debugging tools at least on Windows. A possible future avenue for
|
||||
exploration is the conversion of minidumps to core files, to enable this same
|
||||
benefit on other platforms.
|
||||
|
||||
We have already provided an extension to the minidump format that allows it to
|
||||
carry dumps generated on systems with PowerPC processors. The format already
|
||||
allows for variable CPUs, so our work in this area was limited to defining a
|
||||
context structure sufficient to represent the execution state of a PowerPC. We
|
||||
have also defined an extension that allows minidumps to indicate which thread of
|
||||
execution requested a dump be produced for non-crash dumps.
|
||||
|
||||
Often, the information contained within a dump alone is sufficient to produce a
|
||||
full stack backtrace for each thread. Certain optimizations that compilers
|
||||
employ in producing code frustrate this process. Specifically, the “frame
|
||||
pointer omission” optimization of x86 compilers can make it impossible to
|
||||
produce useful stack traces given only a stack snapshot and CPU context. In
|
||||
these cases, however, compiler-emitted debugging information can aid in
|
||||
producing useful stack traces. The Breakpad processor is able to take advantage
|
||||
of this debugging information as supplied by Microsoft’s C/C++ compiler, the
|
||||
only compiler to apply such optimizations by default. As a result, the Breakpad
|
||||
processor can produce useful stack traces even from code with frame pointer
|
||||
omission optimizations as produced by this compiler.
|
||||
|
||||
### Symbol Files
|
||||
|
||||
The [symbol files](symbol_files.md) that the Breakpad processor accepts allow
|
||||
for frame pointer omission data, but this is only one of their capabilities.
|
||||
Each symbol file also includes information about the functions, source files,
|
||||
and source code line numbers for a single module of code. A module is an
|
||||
individually-loadble chunk of code: these can be executables containing a main
|
||||
program (`exe` files on Windows) or shared libraries (`.so` files on Linux,
|
||||
`.dylib` files, frameworks, and bundles on Mac OS X, and `.dll` files on
|
||||
Windows). Dumps contain information about which of these modules were loaded at
|
||||
the time the dump was produced, and given this information, the Breakpad
|
||||
processor attempts to locate debugging symbols for the module through a
|
||||
user-supplied function embodied in a “symbol supplier.” Breakpad includes a
|
||||
sample symbol supplier, called `SimpleSymbolSupplier`, that is used by its
|
||||
command-line tools; this supplier locates symbol files by pathname.
|
||||
`SimpleSymbolSupplier` is also available to other users of the Breakpad
|
||||
processor library. This allows for the use of a simple reference implementation,
|
||||
but preserves flexibility for users who may have more demanding symbol file
|
||||
storage needs.
|
||||
|
||||
Breakpad’s symbol file format is text-based, and was defined to be fairly
|
||||
human-readable and to encompass the needs of multiple platforms. The Breakpad
|
||||
processor itself does not operate directly with native symbol formats ([DWARF]
|
||||
(http://dwarf.freestandards.org/) and [STABS]
|
||||
(http://sourceware.org/gdb/current/onlinedocs/stabs.html) on most Unix-like
|
||||
systems, [.pdb files]
|
||||
(http://msdn2.microsoft.com/en-us/library/yd4f8bd1(VS.80).aspx) on Windows),
|
||||
because of the complications in accessing potentially complex symbol formats
|
||||
with slight variations between platforms, stored within different types of
|
||||
binary formats. In the case of `.pdb` files, the debugging format is not even
|
||||
documented. Instead, Breakpad’s symbol files are produced on each platform,
|
||||
using specific debugging APIs where available, to convert native symbols to
|
||||
Breakpad’s cross-platform format.
|
||||
|
||||
### Processing
|
||||
|
||||
Most commonly, a developer will enable an application to use Breakpad by
|
||||
building it with a platform-specific [client “handler”](client_design.md)
|
||||
library. After building the application, the developer will create symbol files
|
||||
for Breakpad’s use using the included `dump_syms` or `symupload` tools, or
|
||||
another suitable tool, and place the symbol files where the processor’s symbol
|
||||
supplier will be able to locate them.
|
||||
|
||||
When a dump file is given to the processor’s `MinidumpProcessor` class, it will
|
||||
read it using its included minidump reader, contained in the `Minidump` family
|
||||
of classes. It will collect information about the operating system and CPU that
|
||||
produced the dump, and determine whether the dump was produced as a result of a
|
||||
crash or at the direct request of the application itself. It then loops over all
|
||||
of the threads in a process, attempting to walk the stack associated with each
|
||||
thread. This process is achieved by the processor’s `Stackwalker` components, of
|
||||
which there are a slightly different implementations for each CPU type that the
|
||||
processor is able to handle dumps from. Beginning with a thread’s context, and
|
||||
possibly using debugging data, the stackwalker produces a list of stack frames,
|
||||
containing each instruction executed in the chain. These instructions are
|
||||
matched up with the modules that contributed them to a process, and the
|
||||
`SymbolSupplier` is invoked to locate a symbol file. The symbol file is given to
|
||||
a `SourceLineResolver`, which matches the instruction up with a specific
|
||||
function name, source file, and line number, resulting in a representation of a
|
||||
stack frame that can easily be used to identify which code was executing.
|
||||
|
||||
The results of processing are made available in a `ProcessState` object, which
|
||||
contains a vector of threads, each containing a vector of stack frames.
|
||||
|
||||
For small-scale use of the Breakpad processor, and for testing and debugging,
|
||||
the `minidump_stackwalk` tool is provided. It invokes the processor and displays
|
||||
the full results of processing, optionally allowing symbols to be provided to
|
||||
the processor by a pathname-based symbol supplier, `SimpleSymbolSupplier`.
|
||||
|
||||
For lower-level testing and debugging, the processor library also includes a
|
||||
`minidump_dump` tool, which walks through an entire minidump file and displays
|
||||
its contents in somewhat readable form.
|
||||
|
||||
### Platform Support
|
||||
|
||||
The Breakpad processor library is able to process dumps produced on Mac OS X
|
||||
systems running on x86, x86-64, and PowerPC processors, on Windows and Linux
|
||||
systems running on x86 or x86-64 processors, and on Android systems running ARM
|
||||
or x86 processors. The processor library itself is written in standard C++, and
|
||||
should function properly in most Unix-like environments. It has been tested on
|
||||
Linux and Mac OS X.
|
||||
|
||||
## Future Plans
|
||||
|
||||
There are currently no firm plans or timetables to implement any of these
|
||||
features, although they are possible avenues for future exploration.
|
||||
|
||||
The symbol file format can be extended to carry information about the locations
|
||||
of parameters and local variables as stored in stack frames and registers, and
|
||||
the processor can use this information to provide enhanced stack traces showing
|
||||
function arguments and variable values.
|
||||
|
||||
On Mac OS X and Linux, we can provide tools to convert files from the minidump
|
||||
format into the native core format. This will enable developers to open dump
|
||||
files in a native debugger, just as they are presently able to do with minidumps
|
||||
on Windows.
|
160
docs/stack_walking.md
Normal file
160
docs/stack_walking.md
Normal file
@ -0,0 +1,160 @@
|
||||
# Introduction
|
||||
|
||||
This page aims to provide a detailed description of how Breakpad produces stack
|
||||
traces from the information contained within a minidump file.
|
||||
|
||||
# Details
|
||||
|
||||
## Starting the Process
|
||||
|
||||
Typically the stack walking process is initiated by instantiating the
|
||||
[MinidumpProcessor]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/minidump_processor.cc)
|
||||
class and calling the [MinidumpProcessor::Process]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/minidump_processor.cc#61)
|
||||
method, providing it a minidump file to process. To produce a useful stack
|
||||
trace, the MinidumpProcessor requires two other objects which are passed in its
|
||||
constructor: a [SymbolSupplier]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/symbol_supplier.h)
|
||||
and a [SourceLineResolverInterface]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h).
|
||||
The SymbolSupplier object is responsible for locating and providing SymbolFiles
|
||||
that match modules from the minidump. The SourceLineResolverInterface is
|
||||
responsible for loading the symbol files and using the information contained
|
||||
within to provide function and source information for stack frames, as well as
|
||||
information on how to unwind from a stack frame to its caller. More detail will
|
||||
be provided on these interactions later.
|
||||
|
||||
A number of data streams are extracted from the minidump to begin stack walking:
|
||||
the list of threads from the process ([MinidumpThreadList]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#335)),
|
||||
the list of modules loaded in the process ([MinidumpModuleList]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#501)),
|
||||
and information about the exception that caused the process to crash
|
||||
([MinidumpException]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#615)).
|
||||
|
||||
## Enumerating Threads
|
||||
|
||||
For each thread in the thread list ([MinidumpThread]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#299)),
|
||||
the thread memory containing the stack for the thread ([MinidumpMemoryRegion]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#236))
|
||||
and the CPU context representing the CPU state of the thread at the time the
|
||||
dump was written ([MinidumpContext]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#171))
|
||||
are extracted from the minidump. If the thread being processed is the thread
|
||||
that produced the exception then a CPU context is obtained from the
|
||||
MinidumpException object instead, which represents the CPU state of the thread
|
||||
at the point of the exception. A stack walker is then instantiated by calling
|
||||
the [Stackwalker::StackwalkerForCPU]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#77)
|
||||
method and passing it the CPU context, the thread memory, the module list, as
|
||||
well as the SymbolSupplier and SourceLineResolverInterface. This method selects
|
||||
the specific !Stackwalker subclass based on the CPU architecture of the provided
|
||||
CPU context and returns an instance of that subclass.
|
||||
|
||||
## Walking a thread's stack
|
||||
|
||||
Once a !Stackwalker instance has been obtained, the processor calls the
|
||||
[Stackwalker::Walk]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h)
|
||||
method to obtain a list of frames representing the stack of this thread. The
|
||||
!Stackwalker starts by calling the GetContextFrame method which returns a
|
||||
StackFrame representing the top of the stack, with CPU state provided by the
|
||||
initial CPU context. From there, the stack walker repeats the following steps
|
||||
for each frame in turn:
|
||||
|
||||
### Finding the Module
|
||||
|
||||
The address of the instruction pointer of the current frame is used to determine
|
||||
which module contains the current frame by calling the module list's
|
||||
[GetModuleForAddress]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/code_modules.h#56)
|
||||
method.
|
||||
|
||||
### Locating Symbols
|
||||
|
||||
If a module is located, the SymbolSupplier is asked to locate symbols
|
||||
corresponding to the module by calling its [GetCStringSymbolData]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/symbol_supplier.h#87)
|
||||
method. Typically this is implemented by using the module's debug filename (the
|
||||
PDB filename for Windows dumps) and debug identifier (a GUID plus one extra
|
||||
digit) as a lookup key. The [SimpleSymbolSupplier]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/simple_symbol_supplier.cc)
|
||||
class simply uses these as parts of a file path to locate a flat file on disk.
|
||||
|
||||
### Loading Symbols
|
||||
|
||||
If a symbol file is located, the SourceLineResolverInterface is then asked to
|
||||
load the symbol file by calling its [LoadModuleUsingMemoryBuffer]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#71)
|
||||
method. The [BasicSourceLineResolver]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/basic_source_line_resolver.cc)
|
||||
implementation parses the text-format [symbol file](symbol_files.md) into
|
||||
in-memory data structures to make lookups by address of function names, source
|
||||
line information, and unwind information easy.
|
||||
|
||||
### Getting source line information
|
||||
|
||||
If a symbol file has been successfully loaded, the SourceLineResolverInterface's
|
||||
[FillSourceLineInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#89)
|
||||
method is called to provide a function name and source line information for the
|
||||
current frame. This is done by subtracting the base address of the module
|
||||
containing the current frame from the instruction pointer of the current frame
|
||||
to obtain a relative virtual address (RVA), which is a code offset relative to
|
||||
the start of the module. This RVA is then used as a lookup into a table of
|
||||
functions ([FUNC lines](SymbolFiles#FUNC_records.md) from the symbol file), each
|
||||
of which has an associated address range (function start address, function
|
||||
size). If a function is found whose address range contains the RVA, then its
|
||||
name is used. The RVA is then used as a lookup into a table of source lines
|
||||
([line records](SymbolFiles#Line_records.md) from the symbol file), each of
|
||||
which also has an associated address range. If a match is found it will provide
|
||||
the file name and source line associated with the current frame. If no match was
|
||||
found in the function table, another table of publicly exported symbols may be
|
||||
consulted ([PUBLIC lines](SymbolFiles#PUBLIC_records.md) from the symbol file).
|
||||
Public symbols contain only a start address, so the lookup simply looks for the
|
||||
nearest symbol that is less than the provided RVA.
|
||||
|
||||
### Finding the caller frame
|
||||
|
||||
To find the next frame in the stack, the !Stackwalker calls its [GetCallerFrame]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#186)
|
||||
method, passing in the current frame. Each !Stackwalker subclass implements
|
||||
GetCallerFrame differently, but there are common patterns.
|
||||
|
||||
Typically the first step is to query the SourceLineResolverInterface for the
|
||||
presence of detailed unwind information. This is done using its
|
||||
[FindWindowsFrameInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#96)
|
||||
and [FindCFIFrameInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#102)
|
||||
methods. These methods look for Windows unwind info extracted from a PDB file
|
||||
([STACK WIN](SymbolFiles#STACK_WIN_records.md) lines from the symbol file), or
|
||||
DWARF CFI extracted from a binary ([STACK CFI](SymbolFiles#STACK_CFI_records.md)
|
||||
lines from the symbol file) respectively. The information covers address ranges,
|
||||
so the RVA of the current frame is used for lookup as with function and source
|
||||
line information.
|
||||
|
||||
If unwind info is found it provides a set of rules to recover the register state
|
||||
of the caller frame given the current register state as well as the thread's
|
||||
stack memory. The rules are evaluated to produce the caller frame.
|
||||
|
||||
If unwind info is not found then the !Stackwalker may resort to other methods.
|
||||
Typically on architectures which specify a frame pointer unwinding by
|
||||
dereferencing the frame pointer is tried next. If that is successful it is used
|
||||
to produce the caller frame.
|
||||
|
||||
If no caller frame was found by any other method most !Stackwalker
|
||||
implementations resort to stack scanning by looking at each word on the stack
|
||||
down to a fixed depth (implemented in the [Stackwalker::ScanForReturnAddress]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#131)
|
||||
method) and using a heuristic to attempt to find a reasonable return address
|
||||
(implemented in the [Stackwalker::InstructionAddressSeemsValid]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#111)
|
||||
method).
|
||||
|
||||
If no caller frame is found or the caller frame seems invalid, stack walking
|
||||
stops. If a caller frame was found then these steps repeat using the new frame
|
||||
as the current frame.
|
497
docs/symbol_files.md
Normal file
497
docs/symbol_files.md
Normal file
@ -0,0 +1,497 @@
|
||||
# Introduction
|
||||
|
||||
Given a minidump file, the Breakpad processor produces stack traces that include
|
||||
function names and source locations. However, minidump files contain only the
|
||||
byte-by-byte contents of threads' registers and stacks, without function names
|
||||
or machine-code-to-source mapping data. The processor consults Breakpad symbol
|
||||
files for the information it needs to produce human-readable stack traces from
|
||||
the binary-only minidump file.
|
||||
|
||||
The platform-specific symbol dumping tools parse the debugging information the
|
||||
compiler provides (whether as DWARF or STABS sections in an ELF file or as
|
||||
stand-alone PDB files), and write that information back out in the Breakpad
|
||||
symbol file format. This format is much simpler and less detailed than compiler
|
||||
debugging information, and values legibility over compactness.
|
||||
|
||||
# Overview
|
||||
|
||||
Breakpad symbol files are ASCII text files, with lines delimited as appropriate
|
||||
for the host platform. Each line is a _record_, divided into fields by single
|
||||
spaces; in some cases, the last field of the record can contain spaces. The
|
||||
first field is a string indicating what sort of record the line represents
|
||||
(except for line records; these are very common, making them the default saves
|
||||
space). Some fields hold decimal or hexadecimal numbers; hexadecimal numbers
|
||||
have no "0x" prefix, and use lower-case letters.
|
||||
|
||||
Breakpad symbol files contain the following record types. With some
|
||||
restrictions, these may appear in any order.
|
||||
|
||||
* A `MODULE` record describes the executable file or shared library from which
|
||||
this data was derived, for use by symbol suppliers. A `MODULE' record should
|
||||
be the first record in the file.
|
||||
|
||||
* A `FILE` record gives a source file name, and assigns it a number by which
|
||||
other records can refer to it.
|
||||
|
||||
* A `FUNC` record describes a function present in the source code.
|
||||
|
||||
* A line record indicates to which source file and line a given range of
|
||||
machine code should be attributed. The line is attributed to the function
|
||||
defined by the most recent `FUNC` record.
|
||||
|
||||
* A `PUBLIC` record gives the address of a linker symbol.
|
||||
|
||||
* A `STACK` record provides information necessary to produce stack traces.
|
||||
|
||||
# `MODULE` records
|
||||
|
||||
A `MODULE` record provides meta-information about the module the symbol file
|
||||
describes. It has the form:
|
||||
|
||||
> `MODULE` _operatingsystem_ _architecture_ _id_ _name_
|
||||
|
||||
For example: `MODULE Linux x86 D3096ED481217FD4C16B29CD9BC208BA0 firefox-bin
|
||||
` These records provide meta-information about the executable or shared library
|
||||
from which this symbol file was generated. A symbol supplier might use this
|
||||
information to find the correct symbol files to use to interpret a given
|
||||
minidump, or to perform other sorts of validation. If present, a `MODULE` record
|
||||
should be the first line in the file.
|
||||
|
||||
The fields are separated by spaces, and cannot contain spaces themselves, except
|
||||
for _name_.
|
||||
|
||||
* The _operatingsystem_ field names the operating system on which the
|
||||
executable or shared library was intended to run. This field should have one
|
||||
of the following values: | **Value** | **Meaning** |
|
||||
|:----------|:--------------------| | Linux | Linux | | mac | Macintosh OSX
|
||||
| | windows | Microsoft Windows |
|
||||
|
||||
* The _architecture_ field indicates what processor architecture the
|
||||
executable or shared library contains machine code for. This field should
|
||||
have one of the following values: | **Value** | **Instruction Set
|
||||
Architecture** | |:----------|:---------------------------------| | x86 |
|
||||
Intel IA-32 | | x86\_64 | AMD64/Intel 64 | | ppc | 32-bit PowerPC | | ppc64
|
||||
| 64-bit PowerPC | | unknown | unknown |
|
||||
|
||||
* The _id_ field is a sequence of hexadecimal digits that identifies the exact
|
||||
executable or library whose contents the symbol file describes. The way in
|
||||
which it is computed varies from platform to platform.
|
||||
|
||||
* The _name_ field contains the base name (the final component of the
|
||||
directory path) of the executable or library. It may contain spaces, and
|
||||
extends to the end of the line.
|
||||
|
||||
# `FILE` records
|
||||
|
||||
A `FILE` record holds a source file name for other records to refer to. It has
|
||||
the form:
|
||||
|
||||
> `FILE` _number_ _name_
|
||||
|
||||
For example: `FILE 2 /home/jimb/mc/in/browser/app/nsBrowserApp.cpp
|
||||
`
|
||||
|
||||
A `FILE` record provides the name of a source file, and assigns it a number
|
||||
which other records (line records, in particular) can use to refer to that file
|
||||
name. The _number_ field is a decimal number. The _name_ field is the name of
|
||||
the file; it may contain spaces.
|
||||
|
||||
# `FUNC` records
|
||||
|
||||
A `FUNC` record describes a source-language function. It has the form:
|
||||
|
||||
> `FUNC` _address_ _size_ _parameter\_size_ _name_
|
||||
|
||||
For example: `FUNC c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&,
|
||||
void**) const
|
||||
`
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
||||
address and length in bytes of the machine code instructions the function
|
||||
occupies. (Breakpad symbol files cannot accurately describe functions whose code
|
||||
is not contiguous.) The start address is relative to the module's load address.
|
||||
|
||||
The _parameter\_size_ field is a hexadecimal number indicating the size, in
|
||||
bytes, of the arguments pushed on the stack for this function. Some calling
|
||||
conventions, like the Microsoft Windows `stdcall` convention, require the called
|
||||
function to pop parameters passed to it on the stack from its caller before
|
||||
returning. The stack walker uses this value, along with data from `STACK`
|
||||
records, to step from the called function's frame to the caller's frame.
|
||||
|
||||
The _name_ field is the name of the function. In languages that use linker
|
||||
symbol name mangling like C++, this should be the source language name (the
|
||||
"unmangled" form). This field may contain spaces.
|
||||
|
||||
# Line records
|
||||
|
||||
A line record describes the source file and line number to which a given range
|
||||
of machine code should be attributed. It has the form:
|
||||
|
||||
> _address_ _size_ _line_ _filenum_
|
||||
|
||||
For example: `c184 7 59 4
|
||||
`
|
||||
|
||||
Because they are so common, line records do not begin with a string indicating
|
||||
the record type. All other record types' names use upper-case letters;
|
||||
hexadecimal numbers, like a line record's _address_, use lower-case letters.
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
||||
address and length in bytes of the machine code. The address is relative to the
|
||||
module's load address.
|
||||
|
||||
The _line_ field is the line number to which the machine code should be
|
||||
attributed, in decimal; the first line of the source file is line number 1. The
|
||||
_filenum_ field is a decimal number appearing in a prior `FILE` record; the name
|
||||
given in that record is the source file name for the machine code.
|
||||
|
||||
The line is assumed to belong to the function described by the last preceding
|
||||
`FUNC` record. Line records may not appear before the first `FUNC' record.
|
||||
|
||||
No two line records in a symbol file cover the same range of addresses. However,
|
||||
there may be many line records with identical line and file numbers, as a given
|
||||
source line may contribute many non-contiguous blocks of machine code.
|
||||
|
||||
# `PUBLIC` records
|
||||
|
||||
A `PUBLIC` record describes a publicly visible linker symbol, such as that used
|
||||
to identify an assembly language entry point or region of memory. It has the
|
||||
form:
|
||||
|
||||
> PUBLIC _address_ _parameter\_size_ _name_
|
||||
|
||||
For example: `PUBLIC 2160 0 Public2_1
|
||||
`
|
||||
|
||||
The Breakpad processor essentially treats a `PUBLIC` record as defining a
|
||||
function with no line number data and an indeterminate size: the code extends to
|
||||
the next address mentioned. If a given address is covered by both a `PUBLIC`
|
||||
record and a `FUNC` record, the processor uses the `FUNC` data.
|
||||
|
||||
The _address_ field is a hexadecimal number indicating the symbol's address,
|
||||
relative to the module's load address.
|
||||
|
||||
The _parameter\_size_ field is a hexadecimal number indicating the size of the
|
||||
parameters passed to the code whose entry point the symbol marks, if known. This
|
||||
field has the same meaning as the _parameter\_size_ field of a `FUNC` record;
|
||||
see that description for more details.
|
||||
|
||||
The _name_ field is the name of the symbol. In languages that use linker symbol
|
||||
name mangling like C++, this should be the source language name (the "unmangled"
|
||||
form). This field may contain spaces.
|
||||
|
||||
# `STACK WIN` records
|
||||
|
||||
Given a stack frame, a `STACK WIN` record indicates how to find the frame that
|
||||
called it. It has the form:
|
||||
|
||||
> STACK WIN _type_ _rva_ _code\_size_ _prologue\_size_ _epilogue\_size_
|
||||
> _parameter\_size_ _saved\_register\_size_ _local\_size_ _max\_stack\_size_
|
||||
> _has\_program\_string_ _program\_string\_OR\_allocates\_base\_pointer_
|
||||
|
||||
For example: `STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + =
|
||||
$ebp $ebp ^ =
|
||||
`
|
||||
|
||||
All fields of a `STACK WIN` record, except for the last, are hexadecimal
|
||||
numbers.
|
||||
|
||||
The _type_ field indicates what sort of stack frame data this record holds. Its
|
||||
value should be one of the values of the [StackFrameTypeEnum]
|
||||
(http://msdn.microsoft.com/en-us/library/bc5207xw%28VS.100%29.aspx) type in
|
||||
Microsoft's [Debug Interface Access (DIA)]
|
||||
(http://msdn.microsoft.com/en-us/library/x93ctkx8%28VS.100%29.aspx) API.
|
||||
Breakpad uses only records of type 4 (`FrameTypeFrameData`) and 0
|
||||
(`FrameTypeFPO`); it ignores others. These types differ only in whether the last
|
||||
field is an _allocates\_base\_pointer_ flag (`FrameTypeFPO`) or a program string
|
||||
(`FrameTypeFrameData`). If more than one record covers a given address, Breakpad
|
||||
prefers `FrameTypeFrameData` records over `FrameTypeFPO` records.
|
||||
|
||||
The _rva_ and _code\_size_ fields give the starting address and length in bytes
|
||||
of the machine code covered by this record. The starting address is relative to
|
||||
the module's load address.
|
||||
|
||||
The _prologue\_size_ and _epilogue\_size_ fields give the length, in bytes, of
|
||||
the prologue and epilogue machine code within the record's range. Breakpad does
|
||||
not use these values.
|
||||
|
||||
The _parameter\_size_ field gives the number of argument bytes this function
|
||||
expects to have been passed. This field has the same meaning as the
|
||||
_parameter\_size_ field of a `FUNC` record; see that description for more
|
||||
details.
|
||||
|
||||
The _saved\_register\_size_ field gives the number of bytes in the stack frame
|
||||
dedicated to preserving the values of any callee-saves registers used by this
|
||||
function.
|
||||
|
||||
The _local\_size_ field gives the number of bytes in the stack frame dedicated
|
||||
to holding the function's local variables and temporary values.
|
||||
|
||||
The _max\_stack\_size_ field gives the maximum number of bytes pushed on the
|
||||
stack in the frame. Breakpad does not use this value.
|
||||
|
||||
If the _has\_program\_string_ field is zero, then the `STACK WIN` record's final
|
||||
field is an _allocates\_base\_pointer_ flag, as a hexadecimal number; this is
|
||||
expected for records whose _type_ is 0. Otherwise, the final field is a program
|
||||
string.
|
||||
|
||||
## Interpreting a `STACK WIN` record
|
||||
|
||||
Given the register values for a frame F, we can find the calling frame as
|
||||
follows:
|
||||
|
||||
* If the _has\_program\_string_ field of a `STACK WIN` record is zero, then
|
||||
the final field is _allocates\_base\_pointer_, a flag indicating whether the
|
||||
frame uses the frame pointer register, `%ebp`, as a general-purpose
|
||||
register.
|
||||
* If _allocates\_base\_pointer_ is true, then `%ebp` does not point to the
|
||||
frame's base address. Instead,
|
||||
* Let _next\_parameter\_size_ be the parameter size of the function
|
||||
frame F called (**not** this record's _parameter\_size_ field), or
|
||||
zero if F is the youngest frame on the stack. You must find this
|
||||
value in F's callee's `FUNC`, `STACK WIN`, or `PUBLIC` records.
|
||||
* Let _frame\_size_ be the sum of the _local\_size_ field, the
|
||||
_saved\_register\_size_ field, and _next\_parameter\_size_. > > With
|
||||
those definitions in place, we can recover the calling frame as
|
||||
follows:
|
||||
* F's return address is at `%esp +`_frame\_size_,
|
||||
* the caller's value of `%ebp` is saved at `%esp
|
||||
+`_next\_parameter\_size_`+`_saved\_register\_size_`- 8`, and
|
||||
* the caller's value of `%esp` just before the call instruction was
|
||||
`%esp +`_frame\_size_`+ 4`. > > (Why do we include
|
||||
_next\_parameter\_size_ in the sum when computing _frame\_size_ and
|
||||
the address of the saved `%ebp`? When a function A has called a
|
||||
function B, the arguments that A pushed for B are considered part of
|
||||
A's stack frame: A's value for `%esp` points at the last argument
|
||||
pushed for B. Thus, we must include the size of those arguments
|
||||
(given by the debugging info for B) along with the size of A's
|
||||
register save area and local variable area (given by the debugging
|
||||
info for A) when computing the overall size of A's frame.)
|
||||
* If _allocates\_base\_pointer_ is false, then F's function doesn't use
|
||||
`%ebp` at all. You may recover the calling frame as above, except that
|
||||
the caller's value of `%ebp` is the same as F's value for `%ebp`, so no
|
||||
steps are necessary to recover it.
|
||||
* If the _has\_program\_string_ field of a `STACK WIN` record is not zero,
|
||||
then the record's final field is a string containing a program to be
|
||||
interpreted to recover the caller's frame. The comments in the
|
||||
[postfix\_evaluator.h]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/postfix_evaluator.h#40)
|
||||
header file explain the language in which the program is written. You should
|
||||
place the following variables in the dictionary before interpreting the
|
||||
program:
|
||||
* `$ebp` and `$esp` should be the values of the `%ebp` and `%esp`
|
||||
registers in F.
|
||||
* `.cbParams`, `.cbSavedRegs`, and `.cbLocals`, should be the values of
|
||||
the `STACK WIN` record's _parameter\_size_, _saved\_register\_size_, and
|
||||
_local\_size_ fields.
|
||||
* `.raSearchStart` should be set to the address on the stack to begin
|
||||
scanning for a return address, if necessary. The Breakpad processor sets
|
||||
this to the value of `%esp` in F, plus the _frame\_size_ value mentioned
|
||||
above.
|
||||
|
||||
> If the program stores values for `$eip`, `$esp`, `$ebp`, `$ebx`, `$esi`, or
|
||||
> `$edi`, then those are the values of the given registers in the caller. If the
|
||||
> value of `$eip` is zero, that indicates that the end of the stack has been
|
||||
> reached.
|
||||
|
||||
The Breakpad processor checks that the value yielded by the above for the
|
||||
calling frame's instruction address refers to known code; if the address seems
|
||||
to be bogus, then it uses a heuristic search to find F's return address and
|
||||
stack base.
|
||||
|
||||
# `STACK CFI` records
|
||||
|
||||
`STACK CFI` ("Call Frame Information") records describe how to walk the stack
|
||||
when execution is at a given machine instruction. These records take one of two
|
||||
forms:
|
||||
|
||||
> `STACK CFI INIT` _address_ _size_ _register<sub>1</sub>_:
|
||||
> _expression<sub>1</sub>_ _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
|
||||
>
|
||||
> `STACK CFI` _address_ _register<sub>1</sub>_: _expression<sub>1</sub>_
|
||||
> _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
STACK CFI INIT 804c4b0 40 .cfa: $esp 4 + $eip: .cfa 4 - ^
|
||||
STACK CFI 804c4b1 .cfa: $esp 8 + $ebp: .cfa 8 - ^
|
||||
```
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers. Each
|
||||
_register_<sub>i</sub> is the name of a register or pseudoregister. Each
|
||||
_expression_ is a Breakpad postfix expression, which may contain spaces, but
|
||||
never ends with a colon. (The appropriate register names for a given
|
||||
architecture are determined when `STACK CFI` records are first enabled for that
|
||||
architecture, and should be documented in the appropriate
|
||||
`stackwalker_`_architecture_`.cc` source file.)
|
||||
|
||||
STACK CFI records describe, at each machine instruction in a given function, how
|
||||
to recover the values the machine registers had in the function's caller.
|
||||
Naturally, some registers' values are simply lost, but there are three cases in
|
||||
which they can be recovered:
|
||||
|
||||
* You can always recover the program counter, because that's the function's
|
||||
return address. If the function is ever going to return, the PC must be
|
||||
saved somewhere.
|
||||
|
||||
* You can always recover the stack pointer. The function is responsible for
|
||||
popping its stack frame before it returns to the caller, so it must be able
|
||||
to restore this, as well.
|
||||
|
||||
* You should be able to recover the values of callee-saves registers. These
|
||||
are registers whose values the callee must preserve, either by saving them
|
||||
in its own stack frame before using them and re-loading them before
|
||||
returning, or by not using them at all.
|
||||
|
||||
(As an exception, note that functions which never return may not save any of
|
||||
this data. It may not be possible to walk the stack past such functions' stack
|
||||
frames.)
|
||||
|
||||
Given rules for recovering the values of a function's caller's registers, we can
|
||||
walk up the stack. Starting with the current set of registers --- the PC of the
|
||||
instruction we're currently executing, the current stack pointer, etc. --- we
|
||||
use CFI to recover the values those registers had in the caller of the current
|
||||
frame. This gives us a PC in the caller whose CFI we can look up; we apply the
|
||||
process again to find that function's caller; and so on.
|
||||
|
||||
Concretely, CFI records represent a table with a row for each machine
|
||||
instruction address and a column for each register. The table entry for a given
|
||||
address and register contains a rule describing how, when the PC is at that
|
||||
address, to restore the value that register had in the caller.
|
||||
|
||||
There are some special columns:
|
||||
|
||||
* A column named `.cfa`, for "Canonical Frame Address", tells how to compute
|
||||
the base address of the frame; other entries can refer to the CFA in their
|
||||
rules.
|
||||
|
||||
* A column named `.ra` represents the return address.
|
||||
|
||||
For example, suppose we have a machine with 32-bit registers, one-byte
|
||||
instructions, a stack that grows downwards, and an assembly language that
|
||||
resembles C. Suppose further that we have a function whose machine code looks
|
||||
like this:
|
||||
|
||||
```
|
||||
func: ; entry point; return address at sp
|
||||
func+0: sp -= 16 ; allocate space for stack frame
|
||||
func+1: sp[12] = r0 ; save 4-byte r0 at sp+12
|
||||
... ; stuff that doesn't affect stack
|
||||
func+10: sp -= 4; *sp = x ; push some 4-byte x on the stack
|
||||
... ; stuff that doesn't affect stack
|
||||
func+20: r0 = sp[16] ; restore saved r0
|
||||
func+21: sp += 20 ; pop whole stack frame
|
||||
func+22: pc = *sp; sp += 4 ; pop return address and jump to it
|
||||
```
|
||||
|
||||
The following table would describe the function above:
|
||||
|
||||
**code address** | **.cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **.ra**
|
||||
:--------------- | :------- | :---------------------- | :---------------------- | :-- | :-------
|
||||
func+0 | sp | | | | `cfa[0]`
|
||||
func+1 | sp+16 | | | | `cfa[0]`
|
||||
func+2 | sp+16 | `cfa[-4]` | | | `cfa[0]`
|
||||
func+11 | sp+20 | `cfa[-4]` | | | `cfa[0]`
|
||||
func+21 | sp+20 | | | | `cfa[0]`
|
||||
func+22 | sp | | | | `cfa[0]`
|
||||
|
||||
Some things to note here:
|
||||
|
||||
* Each row describes the state of affairs **before** executing the instruction
|
||||
at the given address. Thus, the row for func+0 describes the state before we
|
||||
execute the first instruction, which allocates the stack frame. In the next
|
||||
row, the formula for computing the CFA has changed, reflecting the
|
||||
allocation.
|
||||
|
||||
* The other entries are written in terms of the CFA; this allows them to
|
||||
remain unchanged as the stack pointer gets bumped around. For example, to
|
||||
find the caller's value for r0 (on Google Code) at func+2, we would first
|
||||
compute the CFA by adding 16 to the sp, and then subtract four from that to
|
||||
find the address at which r0 (on Google Code) was saved.
|
||||
|
||||
* Although the example doesn't show this, most calling conventions designate
|
||||
"callee-saves" and "caller-saves" registers. The callee must restore the
|
||||
values of "callee-saves" registers before returning (if it uses them at
|
||||
all), whereas the callee is free to use "caller-saves" registers without
|
||||
restoring their values. A function that uses caller-saves registers
|
||||
typically does not save their original values at all; in this case, the CFI
|
||||
marks such registers' values as "unrecoverable".
|
||||
|
||||
* Exactly where the CFA points in the frame --- at the return address? below
|
||||
it? At some fixed point within the frame? --- is a question of definition
|
||||
that depends on the architecture and ABI in use. But by definition, the CFA
|
||||
remains constant throughout the lifetime of the frame. It's up to
|
||||
architecture- specific code to know what significance to assign the CFA, if
|
||||
any.
|
||||
|
||||
To save space, the most common type of CFI record only mentions the table
|
||||
entries at which changes take place. So for the above, the CFI data would only
|
||||
actually mention the non-blank entries here:
|
||||
|
||||
**insn** | **cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **ra**
|
||||
:------- | :------ | :---------------------- | :---------------------- | :-- | :-------
|
||||
func+0 | sp | | | | `cfa[0]`
|
||||
func+1 | sp+16 | | | |
|
||||
func+2 | | `cfa[-4]` | | |
|
||||
func+11 | sp+20 | | | |
|
||||
func+21 | | r0 (on Google Code) | | |
|
||||
func+22 | sp | | | |
|
||||
|
||||
A `STACK CFI INIT` record indicates that, at the machine instruction at
|
||||
_address_, belonging to some function, the value that _register<sub>n</sub>_ had
|
||||
in that function's caller can be recovered by evaluating
|
||||
_expression<sub>n</sub>_. The values of any callee-saves registers not mentioned
|
||||
are assumed to be unchanged. (`STACK CFI` records never mention caller-saves
|
||||
registers.) These rules apply starting at _address_ and continue up to, but not
|
||||
including, the address given in the next `STACK CFI` record. The _size_ field is
|
||||
the total number of bytes of machine code covered by this record and any
|
||||
subsequent `STACK CFI` records (until the next `STACK CFI INIT` record). The
|
||||
_address_ field is relative to the module's load address.
|
||||
|
||||
A `STACK CFI` record (no `INIT`) is the same, except that it mentions only those
|
||||
registers whose recovery rules have changed from the previous CFI record. There
|
||||
must be a prior `STACK CFI INIT` or `STACK CFI` record in the symbol file. The
|
||||
_address_ field of this record must be greater than that of the previous record,
|
||||
and it must not be at or beyond the end of the range given by the most recent
|
||||
`STACK CFI INIT` record. The address is relative to the module's load address.
|
||||
|
||||
Each expression is a breakpad-style postfix expression. Expressions may contain
|
||||
spaces, but their tokens may not end with colons. When an expression mentions a
|
||||
register, it refers to the value of that register in the callee, even if a prior
|
||||
name/expression pair gives that register's value in the caller. The exception is
|
||||
`.cfa`, which refers to the canonical frame address computed by the .cfa rule in
|
||||
force at the current instruction.
|
||||
|
||||
The special expression `.undef` indicates that the given register's value cannot
|
||||
be recovered.
|
||||
|
||||
The register names preceding the expressions are always followed by colons. The
|
||||
expressions themselves never contain tokens ending with colons.
|
||||
|
||||
There are two special register names:
|
||||
|
||||
* `.cfa` ("Canonical Frame Address") is the base address of the stack frame.
|
||||
Other registers' rules may refer to this. If no rule is provided for the
|
||||
stack pointer, the value of `.cfa` is the caller's stack pointer.
|
||||
|
||||
* `.ra` is the return address. This is the value of the restored program
|
||||
counter. We use `.ra` instead of the architecture-specific name for the
|
||||
program counter.
|
||||
|
||||
The Breakpad stack walker requires that there be rules in force for `.cfa` and
|
||||
`.ra` at every code address from which it unwinds. If those rules are not
|
||||
present, the stack walker will ignore the `STACK CFI` data, and try to use a
|
||||
different strategy.
|
||||
|
||||
So the CFI for the example function above would be as follows, if `func` were at
|
||||
address 0x1000 (relative to the module's load address):
|
||||
|
||||
```
|
||||
STACK CFI INIT 1000 .cfa: $sp .ra: .cfa ^
|
||||
STACK CFI 1001 .cfa: $sp 16 +
|
||||
STACK CFI 1002 $r0: .cfa 4 - ^
|
||||
STACK CFI 100b .cfa: $sp 20 +
|
||||
STACK CFI 1015 $r0: $r0
|
||||
STACK CFI 1016 .cfa: $sp
|
||||
```
|
70
docs/windows_client_integration.md
Normal file
70
docs/windows_client_integration.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Windows Integration overview
|
||||
|
||||
## Windows Client Code
|
||||
|
||||
The Windows client code is in the `src/client/windows` directory of the tree.
|
||||
Since the header files are fairly well commented some specifics are purposely
|
||||
omitted from this document.
|
||||
|
||||
## Integration of minidump-generation
|
||||
|
||||
Once you build the solution inside `src/client/windows`, an output file of
|
||||
`exception_handler.lib` will be generated. You can either check this into your
|
||||
project's directory or build directly from the source, as the project itself
|
||||
does.
|
||||
|
||||
Enabling Breakpad in your application requires you to `#include
|
||||
"exception_handler.h"` and instantiate the `ExceptionHandler` object like so:
|
||||
|
||||
```
|
||||
handler = new ExceptionHandler(const wstring& dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
int handler_types,
|
||||
MINIDUMP_TYPE dump_type,
|
||||
const wchar_t* pipe_name,
|
||||
const CustomClientInfo* custom_info);
|
||||
```
|
||||
|
||||
The parameters, in order, are:
|
||||
|
||||
* pathname for minidumps to be written to - this is ignored if OOP dump
|
||||
generation is used
|
||||
* A callback that is called when the exception is first handled - you can
|
||||
return true/false here to continue/stop exception processing
|
||||
* A callback that is called after minidumps have been written
|
||||
* Context for the callbacks
|
||||
* Which exceptions to handle - see `HandlerType` enumeration in
|
||||
exception\_handler.h
|
||||
* The type of minidump to generate, using the `MINIDUMP_TYPE` definitions in
|
||||
`DbgHelp.h`
|
||||
* A pipe name that can be used to communicate with a crash generation server
|
||||
* A pointer to a CustomClientInfo class that can be used to send custom data
|
||||
along with the minidump when using OOP generation
|
||||
|
||||
You can also see `src/client/windows/tests/crash_generation_app/*` for a sample
|
||||
app that uses OOP generation.
|
||||
|
||||
## OOP Minidump Generation
|
||||
|
||||
For out of process minidump generation, more work is needed. If you look inside
|
||||
`src/client/windows/crash_generation`, you will see a file called
|
||||
`crash_generation_server.h`. This file is the interface for a crash generation
|
||||
server, which must be instantiated with the same pipe name that is passed to the
|
||||
client above. The logistics of running a separate process that instantiates the
|
||||
crash generation server is left up to you, however.
|
||||
|
||||
## Build process specifics(symbol generation, upload)
|
||||
|
||||
The symbol creation step is talked about in the general overview doc, since it
|
||||
doesn't vary much by platform. You'll need to make sure that the symbols are
|
||||
available wherever minidumps are uploaded to for processing.
|
||||
|
||||
## Out in the field - uploading the minidump
|
||||
|
||||
Inside `src/client/windows/sender` is a class implementation called
|
||||
`CrashReportSender`. This class can be compiled into a separate standalone CLI
|
||||
or in the crash generation server and used to upload the report; it can know
|
||||
when to do so via one of the callbacks provided by the `CrashGenerationServer`
|
||||
or the `ExceptionHandler` object for in-process generation.
|
558
m4/ax_cxx_compile_stdcxx.m4
Normal file
558
m4/ax_cxx_compile_stdcxx.m4
Normal file
@ -0,0 +1,558 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXXFLAGS to
|
||||
# enable support. VERSION may be '11' (for the C++11 standard) or '14'
|
||||
# (for the C++14 standard).
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
# required and that the macro should error out if no mode with that
|
||||
# support is found. If specified 'optional', then configuration proceeds
|
||||
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||
# supporting mode is found.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 1
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [],
|
||||
[$1], [14], [],
|
||||
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
[$2], [noext], [],
|
||||
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=gnu++$1 -std=gnu++0x; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
m4_if([$2], [ext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
AC_LANG_POP([C++])
|
||||
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
|
||||
fi
|
||||
else
|
||||
if test x$ac_success = xno; then
|
||||
HAVE_CXX$1=0
|
||||
AC_MSG_NOTICE([No compiler with C++$1 support was found])
|
||||
else
|
||||
HAVE_CXX$1=1
|
||||
AC_DEFINE(HAVE_CXX$1,1,
|
||||
[define if the compiler supports basic C++$1 syntax])
|
||||
fi
|
||||
|
||||
AC_SUBST(HAVE_CXX$1)
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
dnl Test body for checking C++11 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++11, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201103L
|
||||
|
||||
#error "This is not a C++11 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx11
|
||||
{
|
||||
|
||||
namespace test_static_assert
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_final_override
|
||||
{
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_double_right_angle_brackets
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
struct check {};
|
||||
|
||||
typedef check<void> single_type;
|
||||
typedef check<check<void>> double_type;
|
||||
typedef check<check<check<void>>> triple_type;
|
||||
typedef check<check<check<check<void>>>> quadruple_type;
|
||||
|
||||
}
|
||||
|
||||
namespace test_decltype
|
||||
{
|
||||
|
||||
int
|
||||
f()
|
||||
{
|
||||
int a = 1;
|
||||
decltype(a) b = 2;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_type_deduction
|
||||
{
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
auto
|
||||
add(T1 a1, T2 a2) -> decltype(a1 + a2)
|
||||
{
|
||||
return a1 + a2;
|
||||
}
|
||||
|
||||
int
|
||||
test(const int c, volatile int v)
|
||||
{
|
||||
static_assert(is_same<int, decltype(0)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(c)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(v)>::value == false, "");
|
||||
auto ac = c;
|
||||
auto av = v;
|
||||
auto sumi = ac + av + 'x';
|
||||
auto sumf = ac + av + 1.0;
|
||||
static_assert(is_same<int, decltype(ac)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(av)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumi)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumf)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
|
||||
return (sumf > 0.0) ? sumi : add(c, v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_noexcept
|
||||
{
|
||||
|
||||
int f() { return 0; }
|
||||
int g() noexcept { return 0; }
|
||||
|
||||
static_assert(noexcept(f()) == false, "");
|
||||
static_assert(noexcept(g()) == true, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
|
||||
{
|
||||
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
|
||||
}
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
return strlen_c_r(s, 0UL);
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("1") == 1UL, "");
|
||||
static_assert(strlen_c("example") == 7UL, "");
|
||||
static_assert(strlen_c("another\0example") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_rvalue_references
|
||||
{
|
||||
|
||||
template < int N >
|
||||
struct answer
|
||||
{
|
||||
static constexpr int value = N;
|
||||
};
|
||||
|
||||
answer<1> f(int&) { return answer<1>(); }
|
||||
answer<2> f(const int&) { return answer<2>(); }
|
||||
answer<3> f(int&&) { return answer<3>(); }
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i = 0;
|
||||
const int c = 0;
|
||||
static_assert(decltype(f(i))::value == 1, "");
|
||||
static_assert(decltype(f(c))::value == 2, "");
|
||||
static_assert(decltype(f(0))::value == 3, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_uniform_initialization
|
||||
{
|
||||
|
||||
struct test
|
||||
{
|
||||
static const int zero {};
|
||||
static const int one {1};
|
||||
};
|
||||
|
||||
static_assert(test::zero == 0, "");
|
||||
static_assert(test::one == 1, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambdas
|
||||
{
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
auto lambda1 = [](){};
|
||||
auto lambda2 = lambda1;
|
||||
lambda1();
|
||||
lambda2();
|
||||
}
|
||||
|
||||
int
|
||||
test2()
|
||||
{
|
||||
auto a = [](int i, int j){ return i + j; }(1, 2);
|
||||
auto b = []() -> int { return '0'; }();
|
||||
auto c = [=](){ return a + b; }();
|
||||
auto d = [&](){ return c; }();
|
||||
auto e = [a, &b](int x) mutable {
|
||||
const auto identity = [](int y){ return y; };
|
||||
for (auto i = 0; i < a; ++i)
|
||||
a += b--;
|
||||
return x + identity(a + b);
|
||||
}(0);
|
||||
return a + b + c + d + e;
|
||||
}
|
||||
|
||||
int
|
||||
test3()
|
||||
{
|
||||
const auto nullary = [](){ return 0; };
|
||||
const auto unary = [](int x){ return x; };
|
||||
using nullary_t = decltype(nullary);
|
||||
using unary_t = decltype(unary);
|
||||
const auto higher1st = [](nullary_t f){ return f(); };
|
||||
const auto higher2nd = [unary](nullary_t f1){
|
||||
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
|
||||
};
|
||||
return higher1st(nullary) + higher2nd(nullary)(unary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_variadic_templates
|
||||
{
|
||||
|
||||
template <int...>
|
||||
struct sum;
|
||||
|
||||
template <int N0, int... N1toN>
|
||||
struct sum<N0, N1toN...>
|
||||
{
|
||||
static constexpr auto value = N0 + sum<N1toN...>::value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct sum<>
|
||||
{
|
||||
static constexpr auto value = 0;
|
||||
};
|
||||
|
||||
static_assert(sum<>::value == 0, "");
|
||||
static_assert(sum<1>::value == 1, "");
|
||||
static_assert(sum<23>::value == 23, "");
|
||||
static_assert(sum<1, 2>::value == 3, "");
|
||||
static_assert(sum<5, 5, 11>::value == 21, "");
|
||||
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
|
||||
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
|
||||
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
|
||||
// because of this.
|
||||
namespace test_template_alias_sfinae
|
||||
{
|
||||
|
||||
struct foo {};
|
||||
|
||||
template<typename T>
|
||||
using member = typename T::member_type;
|
||||
|
||||
template<typename T>
|
||||
void func(...) {}
|
||||
|
||||
template<typename T>
|
||||
void func(member<T>*) {}
|
||||
|
||||
void test();
|
||||
|
||||
void test() { func<foo>(0); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx11
|
||||
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++14
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++14, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201402L
|
||||
|
||||
#error "This is not a C++14 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx14
|
||||
{
|
||||
|
||||
namespace test_polymorphic_lambdas
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
const auto lambda = [](auto&&... args){
|
||||
const auto istiny = [](auto x){
|
||||
return (sizeof(x) == 1UL) ? 1 : 0;
|
||||
};
|
||||
const int aretiny[] = { istiny(args)... };
|
||||
return aretiny[0];
|
||||
};
|
||||
return lambda(1, 1L, 1.0f, '1');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_binary_literals
|
||||
{
|
||||
|
||||
constexpr auto ivii = 0b0000000000101010;
|
||||
static_assert(ivii == 42, "wrong value");
|
||||
|
||||
}
|
||||
|
||||
namespace test_generalized_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
constexpr unsigned long
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
auto length = 0UL;
|
||||
for (auto p = s; *p; ++p)
|
||||
++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("x") == 1UL, "");
|
||||
static_assert(strlen_c("test") == 4UL, "");
|
||||
static_assert(strlen_c("another\0test") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_init_capture
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
const auto lambda1 = [a = x](int b){ return a + b; };
|
||||
const auto lambda2 = [a = lambda1(x)](){ return a; };
|
||||
return lambda2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_seperators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
static_assert(ten_million == 100000000, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_return_type_deduction
|
||||
{
|
||||
|
||||
auto f(int& x) { return x; }
|
||||
decltype(auto) g(int& x) { return x; }
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static constexpr auto value = true;
|
||||
};
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
static_assert(is_same<int, decltype(f(x))>::value, "");
|
||||
static_assert(is_same<int&, decltype(g(x))>::value, "");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx14
|
||||
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
283
m4/ax_pthread.m4
Normal file
283
m4/ax_pthread.m4
Normal file
@ -0,0 +1,283 @@
|
||||
# ===========================================================================
|
||||
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro figures out how to build C programs using POSIX threads. It
|
||||
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
||||
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
||||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also link it with them as well. e.g. you should link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threads programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
|
||||
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
#
|
||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
||||
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
||||
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
||||
# will define HAVE_PTHREAD.
|
||||
#
|
||||
# Please let the authors know if this macro fails on any platform, or if
|
||||
# you have any other suggestions or comments. This macro was based on work
|
||||
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
||||
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
||||
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
||||
# grateful for the helpful feedback of numerous users.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 6
|
||||
|
||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||
AC_DEFUN([AX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ax_pthread_ok=no
|
||||
|
||||
# We used to check for pthread.h first, but this fails if pthread.h
|
||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
||||
# It gets checked for in the link test anyway.
|
||||
|
||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||
# etcetera environment variables, and if threads linking works using
|
||||
# them:
|
||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
||||
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
|
||||
AC_MSG_RESULT($ax_pthread_ok)
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
fi
|
||||
|
||||
# We must check for the threads library under a number of different
|
||||
# names; the ordering is very important because some systems
|
||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
# The ordering *is* (sometimes) important. Some notes on the
|
||||
# individual items follow:
|
||||
|
||||
# pthreads: AIX (must check this before -lpthread)
|
||||
# none: in case threads are in libc; should be tried before -Kthread and
|
||||
# other compiler flags to prevent continual compiler warnings
|
||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
||||
# -pthreads: Solaris/gcc
|
||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
||||
# also defines -D_REENTRANT)
|
||||
# ... -mt is also the pthreads flag for HP/aCC
|
||||
# pthread: Linux, etcetera
|
||||
# --thread-safe: KAI C++
|
||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*solaris*)
|
||||
|
||||
# On Solaris (at least, for some versions), libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
||||
# a function called by this macro, so we could check for that, but
|
||||
# who knows whether they'll stub that too in a future libc.) So,
|
||||
# we'll just look for -pthreads and -lpthread first:
|
||||
|
||||
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
|
||||
;;
|
||||
|
||||
*-darwin*)
|
||||
acx_pthread_flags="-pthread $acx_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
for flag in $ax_pthread_flags; do
|
||||
|
||||
case $flag in
|
||||
none)
|
||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-*)
|
||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
|
||||
if test x"$ax_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Check for various functions. We must include pthread.h,
|
||||
# since some functions may be macros. (On the Sequent, we
|
||||
# need a special flag -Kthread to make this header compile.)
|
||||
# We check for pthread_join because it is in -lpthread on IRIX
|
||||
# while pthread_create is in libc. We check for pthread_attr_init
|
||||
# due to DEC craziness with -lpthreads. We check for
|
||||
# pthread_cleanup_push because it is one of the few pthread
|
||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||
# We try pthread_create on general principles.
|
||||
AC_TRY_LINK([#include <pthread.h>
|
||||
static void routine(void* a) {a=0;}
|
||||
static void* start_routine(void* a) {return a;}],
|
||||
[pthread_t th; pthread_attr_t attr;
|
||||
pthread_join(th, 0);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_cleanup_push(routine, 0);
|
||||
pthread_create(&th,0,start_routine,0);
|
||||
pthread_cleanup_pop(0); ],
|
||||
[ax_pthread_ok=yes])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT($ax_pthread_ok)
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
||||
attr_name=unknown
|
||||
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
|
||||
[attr_name=$attr; break])
|
||||
done
|
||||
AC_MSG_RESULT($attr_name)
|
||||
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
|
||||
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
|
||||
[Define to necessary symbol if this constant
|
||||
uses a non-standard name on your system.])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
||||
flag=no
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
|
||||
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
|
||||
esac
|
||||
AC_MSG_RESULT(${flag})
|
||||
if test "x$flag" != xno; then
|
||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
||||
fi
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
# More AIX lossage: must compile with xlc_r or cc_r
|
||||
if test x"$GCC" != xyes; then
|
||||
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
|
||||
else
|
||||
PTHREAD_CC=$CC
|
||||
fi
|
||||
else
|
||||
PTHREAD_CC="$CC"
|
||||
fi
|
||||
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_CC)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test x"$ax_pthread_ok" = xyes; then
|
||||
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
|
||||
:
|
||||
else
|
||||
ax_pthread_ok=no
|
||||
$2
|
||||
fi
|
||||
AC_LANG_RESTORE
|
||||
])dnl AX_PTHREAD
|
7377
m4/libtool.m4
vendored
Normal file
7377
m4/libtool.m4
vendored
Normal file
File diff suppressed because it is too large
Load Diff
368
m4/ltoptions.m4
vendored
Normal file
368
m4/ltoptions.m4
vendored
Normal file
@ -0,0 +1,368 @@
|
||||
# Helper functions for option handling. -*- Autoconf -*-
|
||||
#
|
||||
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 6 ltoptions.m4
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
|
||||
|
||||
|
||||
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
|
||||
# ------------------------------------------
|
||||
m4_define([_LT_MANGLE_OPTION],
|
||||
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
|
||||
|
||||
|
||||
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
|
||||
# ---------------------------------------
|
||||
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
|
||||
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
|
||||
# saved as a flag.
|
||||
m4_define([_LT_SET_OPTION],
|
||||
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
|
||||
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
|
||||
_LT_MANGLE_DEFUN([$1], [$2]),
|
||||
[m4_warning([Unknown $1 option `$2'])])[]dnl
|
||||
])
|
||||
|
||||
|
||||
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
|
||||
# ------------------------------------------------------------
|
||||
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
|
||||
m4_define([_LT_IF_OPTION],
|
||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
|
||||
|
||||
|
||||
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
|
||||
# -------------------------------------------------------
|
||||
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
|
||||
# are set.
|
||||
m4_define([_LT_UNLESS_OPTIONS],
|
||||
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
|
||||
[m4_define([$0_found])])])[]dnl
|
||||
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
|
||||
])[]dnl
|
||||
])
|
||||
|
||||
|
||||
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
|
||||
# ----------------------------------------
|
||||
# OPTION-LIST is a space-separated list of Libtool options associated
|
||||
# with MACRO-NAME. If any OPTION has a matching handler declared with
|
||||
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
|
||||
# the unknown option and exit.
|
||||
m4_defun([_LT_SET_OPTIONS],
|
||||
[# Set options
|
||||
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
||||
[_LT_SET_OPTION([$1], _LT_Option)])
|
||||
|
||||
m4_if([$1],[LT_INIT],[
|
||||
dnl
|
||||
dnl Simply set some default values (i.e off) if boolean options were not
|
||||
dnl specified:
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
|
||||
])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
|
||||
])
|
||||
dnl
|
||||
dnl If no reference was made to various pairs of opposing options, then
|
||||
dnl we run the default mode handler for the pair. For example, if neither
|
||||
dnl `shared' nor `disable-shared' was passed, we enable building of shared
|
||||
dnl archives by default:
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
|
||||
[_LT_ENABLE_FAST_INSTALL])
|
||||
])
|
||||
])# _LT_SET_OPTIONS
|
||||
|
||||
|
||||
## --------------------------------- ##
|
||||
## Macros to handle LT_INIT options. ##
|
||||
## --------------------------------- ##
|
||||
|
||||
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
|
||||
# -----------------------------------------
|
||||
m4_define([_LT_MANGLE_DEFUN],
|
||||
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
|
||||
|
||||
|
||||
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
|
||||
# -----------------------------------------------
|
||||
m4_define([LT_OPTION_DEFINE],
|
||||
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
|
||||
])# LT_OPTION_DEFINE
|
||||
|
||||
|
||||
# dlopen
|
||||
# ------
|
||||
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
|
||||
])
|
||||
|
||||
AU_DEFUN([AC_LIBTOOL_DLOPEN],
|
||||
[_LT_SET_OPTION([LT_INIT], [dlopen])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the `dlopen' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
|
||||
|
||||
|
||||
# win32-dll
|
||||
# ---------
|
||||
# Declare package support for building win32 dll's.
|
||||
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
|
||||
[enable_win32_dll=yes
|
||||
|
||||
case $host in
|
||||
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
|
||||
AC_CHECK_TOOL(AS, as, false)
|
||||
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
|
||||
AC_CHECK_TOOL(OBJDUMP, objdump, false)
|
||||
;;
|
||||
esac
|
||||
|
||||
test -z "$AS" && AS=as
|
||||
_LT_DECL([], [AS], [0], [Assembler program])dnl
|
||||
|
||||
test -z "$DLLTOOL" && DLLTOOL=dlltool
|
||||
_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
|
||||
|
||||
test -z "$OBJDUMP" && OBJDUMP=objdump
|
||||
_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
|
||||
])# win32-dll
|
||||
|
||||
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
|
||||
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
||||
_LT_SET_OPTION([LT_INIT], [win32-dll])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the `win32-dll' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
|
||||
|
||||
|
||||
# _LT_ENABLE_SHARED([DEFAULT])
|
||||
# ----------------------------
|
||||
# implement the --enable-shared flag, and supports the `shared' and
|
||||
# `disable-shared' LT_INIT options.
|
||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
||||
m4_define([_LT_ENABLE_SHARED],
|
||||
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([shared],
|
||||
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
|
||||
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_shared=yes ;;
|
||||
no) enable_shared=no ;;
|
||||
*)
|
||||
enable_shared=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
||||
for pkg in $enableval; do
|
||||
IFS="$lt_save_ifs"
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_shared=yes
|
||||
fi
|
||||
done
|
||||
IFS="$lt_save_ifs"
|
||||
;;
|
||||
esac],
|
||||
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
|
||||
|
||||
_LT_DECL([build_libtool_libs], [enable_shared], [0],
|
||||
[Whether or not to build shared libraries])
|
||||
])# _LT_ENABLE_SHARED
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
|
||||
|
||||
# Old names:
|
||||
AC_DEFUN([AC_ENABLE_SHARED],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
|
||||
])
|
||||
|
||||
AC_DEFUN([AC_DISABLE_SHARED],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-shared])
|
||||
])
|
||||
|
||||
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
|
||||
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
|
||||
|
||||
|
||||
|
||||
# _LT_ENABLE_STATIC([DEFAULT])
|
||||
# ----------------------------
|
||||
# implement the --enable-static flag, and support the `static' and
|
||||
# `disable-static' LT_INIT options.
|
||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
||||
m4_define([_LT_ENABLE_STATIC],
|
||||
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([static],
|
||||
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
|
||||
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_static=yes ;;
|
||||
no) enable_static=no ;;
|
||||
*)
|
||||
enable_static=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
||||
for pkg in $enableval; do
|
||||
IFS="$lt_save_ifs"
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_static=yes
|
||||
fi
|
||||
done
|
||||
IFS="$lt_save_ifs"
|
||||
;;
|
||||
esac],
|
||||
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
|
||||
|
||||
_LT_DECL([build_old_libs], [enable_static], [0],
|
||||
[Whether or not to build static libraries])
|
||||
])# _LT_ENABLE_STATIC
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
|
||||
|
||||
# Old names:
|
||||
AC_DEFUN([AC_ENABLE_STATIC],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
|
||||
])
|
||||
|
||||
AC_DEFUN([AC_DISABLE_STATIC],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-static])
|
||||
])
|
||||
|
||||
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
|
||||
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
|
||||
|
||||
|
||||
|
||||
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
|
||||
# ----------------------------------
|
||||
# implement the --enable-fast-install flag, and support the `fast-install'
|
||||
# and `disable-fast-install' LT_INIT options.
|
||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
||||
m4_define([_LT_ENABLE_FAST_INSTALL],
|
||||
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([fast-install],
|
||||
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
|
||||
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_fast_install=yes ;;
|
||||
no) enable_fast_install=no ;;
|
||||
*)
|
||||
enable_fast_install=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
||||
for pkg in $enableval; do
|
||||
IFS="$lt_save_ifs"
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_fast_install=yes
|
||||
fi
|
||||
done
|
||||
IFS="$lt_save_ifs"
|
||||
;;
|
||||
esac],
|
||||
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
|
||||
|
||||
_LT_DECL([fast_install], [enable_fast_install], [0],
|
||||
[Whether or not to optimize for fast installation])dnl
|
||||
])# _LT_ENABLE_FAST_INSTALL
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
|
||||
|
||||
# Old names:
|
||||
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
||||
the `fast-install' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
||||
the `disable-fast-install' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
|
||||
|
||||
|
||||
# _LT_WITH_PIC([MODE])
|
||||
# --------------------
|
||||
# implement the --with-pic flag, and support the `pic-only' and `no-pic'
|
||||
# LT_INIT options.
|
||||
# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
|
||||
m4_define([_LT_WITH_PIC],
|
||||
[AC_ARG_WITH([pic],
|
||||
[AS_HELP_STRING([--with-pic],
|
||||
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
|
||||
[pic_mode="$withval"],
|
||||
[pic_mode=default])
|
||||
|
||||
test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
|
||||
|
||||
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
|
||||
])# _LT_WITH_PIC
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
|
||||
|
||||
# Old name:
|
||||
AU_DEFUN([AC_LIBTOOL_PICMODE],
|
||||
[_LT_SET_OPTION([LT_INIT], [pic-only])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the `pic-only' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
|
||||
|
||||
## ----------------- ##
|
||||
## LTDL_INIT Options ##
|
||||
## ----------------- ##
|
||||
|
||||
m4_define([_LTDL_MODE], [])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
|
||||
[m4_define([_LTDL_MODE], [nonrecursive])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
|
||||
[m4_define([_LTDL_MODE], [recursive])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
|
||||
[m4_define([_LTDL_MODE], [subproject])])
|
||||
|
||||
m4_define([_LTDL_TYPE], [])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [installable],
|
||||
[m4_define([_LTDL_TYPE], [installable])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
|
||||
[m4_define([_LTDL_TYPE], [convenience])])
|
123
m4/ltsugar.m4
vendored
Normal file
123
m4/ltsugar.m4
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
|
||||
#
|
||||
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 6 ltsugar.m4
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
|
||||
|
||||
|
||||
# lt_join(SEP, ARG1, [ARG2...])
|
||||
# -----------------------------
|
||||
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
|
||||
# associated separator.
|
||||
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
|
||||
# versions in m4sugar had bugs.
|
||||
m4_define([lt_join],
|
||||
[m4_if([$#], [1], [],
|
||||
[$#], [2], [[$2]],
|
||||
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
|
||||
m4_define([_lt_join],
|
||||
[m4_if([$#$2], [2], [],
|
||||
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
|
||||
|
||||
|
||||
# lt_car(LIST)
|
||||
# lt_cdr(LIST)
|
||||
# ------------
|
||||
# Manipulate m4 lists.
|
||||
# These macros are necessary as long as will still need to support
|
||||
# Autoconf-2.59 which quotes differently.
|
||||
m4_define([lt_car], [[$1]])
|
||||
m4_define([lt_cdr],
|
||||
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
|
||||
[$#], 1, [],
|
||||
[m4_dquote(m4_shift($@))])])
|
||||
m4_define([lt_unquote], $1)
|
||||
|
||||
|
||||
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
|
||||
# ------------------------------------------
|
||||
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
|
||||
# Note that neither SEPARATOR nor STRING are expanded; they are appended
|
||||
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
|
||||
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
|
||||
# than defined and empty).
|
||||
#
|
||||
# This macro is needed until we can rely on Autoconf 2.62, since earlier
|
||||
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
|
||||
m4_define([lt_append],
|
||||
[m4_define([$1],
|
||||
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
|
||||
|
||||
|
||||
|
||||
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
|
||||
# ----------------------------------------------------------
|
||||
# Produce a SEP delimited list of all paired combinations of elements of
|
||||
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
|
||||
# has the form PREFIXmINFIXSUFFIXn.
|
||||
# Needed until we can rely on m4_combine added in Autoconf 2.62.
|
||||
m4_define([lt_combine],
|
||||
[m4_if(m4_eval([$# > 3]), [1],
|
||||
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
|
||||
[[m4_foreach([_Lt_prefix], [$2],
|
||||
[m4_foreach([_Lt_suffix],
|
||||
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
|
||||
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
|
||||
|
||||
|
||||
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
|
||||
# -----------------------------------------------------------------------
|
||||
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
|
||||
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
|
||||
m4_define([lt_if_append_uniq],
|
||||
[m4_ifdef([$1],
|
||||
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
|
||||
[lt_append([$1], [$2], [$3])$4],
|
||||
[$5])],
|
||||
[lt_append([$1], [$2], [$3])$4])])
|
||||
|
||||
|
||||
# lt_dict_add(DICT, KEY, VALUE)
|
||||
# -----------------------------
|
||||
m4_define([lt_dict_add],
|
||||
[m4_define([$1($2)], [$3])])
|
||||
|
||||
|
||||
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
|
||||
# --------------------------------------------
|
||||
m4_define([lt_dict_add_subkey],
|
||||
[m4_define([$1($2:$3)], [$4])])
|
||||
|
||||
|
||||
# lt_dict_fetch(DICT, KEY, [SUBKEY])
|
||||
# ----------------------------------
|
||||
m4_define([lt_dict_fetch],
|
||||
[m4_ifval([$3],
|
||||
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
|
||||
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
|
||||
|
||||
|
||||
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
|
||||
# -----------------------------------------------------------------
|
||||
m4_define([lt_if_dict_fetch],
|
||||
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
|
||||
[$5],
|
||||
[$6])])
|
||||
|
||||
|
||||
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
|
||||
# --------------------------------------------------------------
|
||||
m4_define([lt_dict_filter],
|
||||
[m4_if([$5], [], [],
|
||||
[lt_join(m4_quote(m4_default([$4], [[, ]])),
|
||||
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
|
||||
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
|
||||
])
|
23
m4/ltversion.m4
vendored
Normal file
23
m4/ltversion.m4
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# ltversion.m4 -- version numbers -*- Autoconf -*-
|
||||
#
|
||||
# Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
# Written by Scott James Remnant, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# Generated from ltversion.in.
|
||||
|
||||
# serial 3017 ltversion.m4
|
||||
# This file is part of GNU Libtool
|
||||
|
||||
m4_define([LT_PACKAGE_VERSION], [2.2.6b])
|
||||
m4_define([LT_PACKAGE_REVISION], [1.3017])
|
||||
|
||||
AC_DEFUN([LTVERSION_VERSION],
|
||||
[macro_version='2.2.6b'
|
||||
macro_revision='1.3017'
|
||||
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
|
||||
_LT_DECL(, macro_revision, 0)
|
||||
])
|
92
m4/lt~obsolete.m4
vendored
Normal file
92
m4/lt~obsolete.m4
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
|
||||
#
|
||||
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
|
||||
# Written by Scott James Remnant, 2004.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 4 lt~obsolete.m4
|
||||
|
||||
# These exist entirely to fool aclocal when bootstrapping libtool.
|
||||
#
|
||||
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
|
||||
# which have later been changed to m4_define as they aren't part of the
|
||||
# exported API, or moved to Autoconf or Automake where they belong.
|
||||
#
|
||||
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
|
||||
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
|
||||
# using a macro with the same name in our local m4/libtool.m4 it'll
|
||||
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
|
||||
# and doesn't know about Autoconf macros at all.)
|
||||
#
|
||||
# So we provide this file, which has a silly filename so it's always
|
||||
# included after everything else. This provides aclocal with the
|
||||
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
|
||||
# because those macros already exist, or will be overwritten later.
|
||||
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
|
||||
#
|
||||
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
|
||||
# Yes, that means every name once taken will need to remain here until
|
||||
# we give up compatibility with versions before 1.7, at which point
|
||||
# we need to keep only those names which we still refer to.
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
|
||||
|
||||
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
|
||||
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
|
||||
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
|
||||
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
|
||||
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
|
||||
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
|
||||
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
|
||||
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
|
||||
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
|
||||
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
|
||||
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
|
||||
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
|
||||
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
|
||||
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
|
||||
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
|
||||
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
|
||||
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
|
||||
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
|
||||
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
|
||||
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
|
||||
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
|
||||
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
|
||||
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
|
||||
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
|
||||
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
|
||||
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
|
||||
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
|
||||
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
|
||||
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
|
||||
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
|
||||
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
|
||||
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
|
||||
m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
|
||||
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
|
43
scripts/travis-build.sh
Executable file
43
scripts/travis-build.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
setup_env() {
|
||||
# Travis sets CC/CXX to the system toolchain, so our .travis.yml
|
||||
# exports USE_{CC,CXX} for this script to use.
|
||||
if [ -n "$USE_CC" ]; then
|
||||
export CC=$USE_CC
|
||||
fi
|
||||
if [ -n "$USE_CXX" ]; then
|
||||
export CXX=$USE_CXX
|
||||
fi
|
||||
# Use -jN for faster builds. Travis build machines under Docker
|
||||
# have a lot of cores, but are memory-limited, so the kernel
|
||||
# will OOM if we try to use them all, so use at most 4.
|
||||
# See https://github.com/travis-ci/travis-ci/issues/1972
|
||||
export NCPUS=$(getconf _NPROCESSORS_ONLN)
|
||||
export JOBS=$(( $NCPUS < 4 ? $NCPUS : 4 ))
|
||||
}
|
||||
|
||||
# Do an in-tree build and make sure tests pass.
|
||||
build() {
|
||||
./configure
|
||||
make -j${JOBS} check VERBOSE=1
|
||||
make distclean
|
||||
}
|
||||
|
||||
# Do an out-of-tree build and make sure we can create a release tarball.
|
||||
build_out_of_tree() {
|
||||
mkdir -p build/native
|
||||
cd build/native
|
||||
../../configure
|
||||
make -j${JOBS} distcheck VERBOSE=1
|
||||
}
|
||||
|
||||
main() {
|
||||
setup_env
|
||||
build
|
||||
build_out_of_tree
|
||||
}
|
||||
|
||||
main "$@"
|
24
scripts/travis-checkout.sh
Executable file
24
scripts/travis-checkout.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
get_depot_tools() {
|
||||
cd
|
||||
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
PATH="$HOME/depot_tools:$PATH"
|
||||
}
|
||||
|
||||
gclient_sync() {
|
||||
# Rename the source dir to match what gclient expects.
|
||||
srcdir=$(basename "$TRAVIS_BUILD_DIR")
|
||||
cd "${TRAVIS_BUILD_DIR}"/..
|
||||
mv "${srcdir}" src
|
||||
gclient config --unmanaged https://github.com/google/breakpad.git
|
||||
gclient sync
|
||||
}
|
||||
|
||||
main() {
|
||||
get_depot_tools
|
||||
gclient_sync
|
||||
}
|
||||
|
||||
main "$@"
|
@ -30,7 +30,28 @@
|
||||
#ifndef BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
#define BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
|
||||
// (illegal instruction) instead of SIGSEGV (segmentation fault). Also,
|
||||
// the number of memory regions differs, so there is no point in running
|
||||
// this test if AddressSanitizer is used.
|
||||
//
|
||||
// Ideally we'd use this attribute to disable ASAN on a per-func basis,
|
||||
// but this doesn't seem to actually work, and it's changed names over
|
||||
// time. So just stick with disabling the actual tests.
|
||||
// http://crbug.com/304575
|
||||
//#define NO_ASAN __attribute__((no_sanitize_address))
|
||||
#if defined(__clang__) && defined(__has_feature)
|
||||
// Have to keep this check sep from above as newer gcc will barf on it.
|
||||
# if __has_feature(address_sanitizer)
|
||||
# define ADDRESS_SANITIZER
|
||||
# endif
|
||||
#elif defined(__GNUC__) && defined(__SANITIZE_ADDRESS__)
|
||||
# define ADDRESS_SANITIZER
|
||||
#else
|
||||
# undef ADDRESS_SANITIZER
|
||||
#endif
|
||||
|
||||
#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
|
41
src/build/all.gyp
Normal file
41
src/build/all.gyp
Normal file
@ -0,0 +1,41 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'All',
|
||||
'type': 'none',
|
||||
'dependencies': [
|
||||
'../common/common.gyp:*',
|
||||
'../processor/processor.gyp:*',
|
||||
'../tools/tools.gyp:*',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
1045
src/build/common.gypi
Normal file
1045
src/build/common.gypi
Normal file
File diff suppressed because it is too large
Load Diff
57
src/build/filename_rules.gypi
Normal file
57
src/build/filename_rules.gypi
Normal file
@ -0,0 +1,57 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'target_conditions': [
|
||||
['OS!="win"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)windows/'],
|
||||
],
|
||||
}],
|
||||
['OS!="linux"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)linux/'],
|
||||
],
|
||||
}],
|
||||
['OS!="mac"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)mac/'],
|
||||
],
|
||||
}],
|
||||
['OS!="android"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)android/'],
|
||||
],
|
||||
}],
|
||||
['OS!="solaris"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)solaris/'],
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
67
src/build/gyp_breakpad
Executable file
67
src/build/gyp_breakpad
Executable file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
breakpad_root = os.path.abspath(os.path.join(script_dir, os.pardir))
|
||||
|
||||
sys.path.insert(0, os.path.join(breakpad_root, 'tools', 'gyp', 'pylib'))
|
||||
import gyp
|
||||
|
||||
def run_gyp(args):
|
||||
rc = gyp.main(args)
|
||||
if rc != 0:
|
||||
print 'Error running GYP'
|
||||
sys.exit(rc)
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
args.append(os.path.join(script_dir, 'all.gyp'))
|
||||
|
||||
args.append('-I')
|
||||
args.append(os.path.join(breakpad_root, 'build', 'common.gypi'))
|
||||
|
||||
args.extend(['-D', 'gyp_output_dir=out'])
|
||||
|
||||
# Set the GYP DEPTH variable to the root of the project.
|
||||
args.append('--depth=' + os.path.relpath(breakpad_root))
|
||||
|
||||
print 'Updating projects from gyp files...'
|
||||
sys.stdout.flush()
|
||||
|
||||
run_gyp(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
90
src/build/testing.gypi
Normal file
90
src/build/testing.gypi
Normal file
@ -0,0 +1,90 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'gtest',
|
||||
'type': 'static_library',
|
||||
'sources': [
|
||||
'../testing/gtest/src/gtest-all.cc',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../testing/gtest',
|
||||
'../testing/gtest/include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../testing/gtest/include',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
'target_name': 'gtest_main',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'gtest',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/src/gtest_main.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'gmock',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'gtest',
|
||||
],
|
||||
'sources': [
|
||||
'../testing/src/gmock-all.cc',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../testing',
|
||||
'../testing/include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../testing/include',
|
||||
],
|
||||
},
|
||||
'export_dependent_settings': [
|
||||
'gtest',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'gmock_main',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'gmock',
|
||||
],
|
||||
'sources': [
|
||||
'../testing/src/gmock_main.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
73
src/client/apple/Framework/BreakpadDefines.h
Normal file
73
src/client/apple/Framework/BreakpadDefines.h
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Keys for configuration file
|
||||
#define kReporterMinidumpDirectoryKey "MinidumpDir"
|
||||
#define kReporterMinidumpIDKey "MinidumpID"
|
||||
|
||||
// Filename for recording uploaded IDs
|
||||
#define kReporterLogFilename "uploads.log"
|
||||
|
||||
// The default subdirectory of the Library to put crash dumps in
|
||||
// The subdirectory is
|
||||
// ~/Library/<kDefaultLibrarySubdirectory>/<GoogleBreakpadProduct>
|
||||
#define kDefaultLibrarySubdirectory "Breakpad"
|
||||
|
||||
// Specify some special keys to be used in the configuration file that is
|
||||
// generated by Breakpad and consumed by the crash_sender.
|
||||
#define BREAKPAD_PRODUCT "BreakpadProduct"
|
||||
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
|
||||
#define BREAKPAD_VERSION "BreakpadVersion"
|
||||
#define BREAKPAD_VENDOR "BreakpadVendor"
|
||||
#define BREAKPAD_URL "BreakpadURL"
|
||||
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
|
||||
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
|
||||
#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout"
|
||||
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
|
||||
#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation"
|
||||
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
|
||||
#define BREAKPAD_REPORTER_EXE_LOCATION \
|
||||
"BreakpadReporterExeLocation"
|
||||
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
|
||||
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
|
||||
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
|
||||
#define BREAKPAD_COMMENTS "BreakpadComments"
|
||||
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
|
||||
#define BREAKPAD_EMAIL "BreakpadEmail"
|
||||
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
|
||||
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
|
||||
#define BREAKPAD_IN_PROCESS "BreakpadInProcess"
|
||||
|
||||
// The keys below are NOT user supplied, and are used internally.
|
||||
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime"
|
||||
#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime"
|
||||
#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime"
|
||||
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
|
||||
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_"
|
||||
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand"
|
246
src/client/ios/Breakpad.h
Normal file
246
src/client/ios/Breakpad.h
Normal file
@ -0,0 +1,246 @@
|
||||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Framework to provide a simple C API to crash reporting for
|
||||
// applications. By default, if any machine-level exception (e.g.,
|
||||
// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef
|
||||
// object as follows:
|
||||
//
|
||||
// 1. Create a minidump file (see Breakpad for details)
|
||||
// 2. Create a config file.
|
||||
//
|
||||
// These files can then be uploaded to a server.
|
||||
|
||||
typedef void *BreakpadRef;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include <client/apple/Framework/BreakpadDefines.h>
|
||||
|
||||
// The keys in the dictionary returned by |BreakpadGenerateReport|.
|
||||
#define BREAKPAD_OUTPUT_DUMP_FILE "BreakpadDumpFile"
|
||||
#define BREAKPAD_OUTPUT_CONFIG_FILE "BreakpadConfigFile"
|
||||
|
||||
// Optional user-defined function to decide if we should handle this crash or
|
||||
// forward it along.
|
||||
// Return true if you want Breakpad to handle it.
|
||||
// Return false if you want Breakpad to skip it
|
||||
// The exception handler always returns false, as if SEND_AND_EXIT were false
|
||||
// (which means the next exception handler will take the exception)
|
||||
typedef bool (*BreakpadFilterCallback)(int exception_type,
|
||||
int exception_code,
|
||||
mach_port_t crashing_thread,
|
||||
void *context);
|
||||
|
||||
// Create a new BreakpadRef object and install it as an exception
|
||||
// handler. The |parameters| will typically be the contents of your
|
||||
// bundle's Info.plist.
|
||||
//
|
||||
// You can also specify these additional keys for customizable behavior:
|
||||
// Key: Value:
|
||||
// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
|
||||
// This one is used as the key to identify
|
||||
// the product when uploading. Falls back to
|
||||
// CFBundleName if not specified.
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
|
||||
// name for the product when the crash_sender
|
||||
// pops up UI for the user. Falls back first to
|
||||
// CFBundleDisplayName and then to
|
||||
// BREAKPAD_PRODUCT if not specified.
|
||||
//
|
||||
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
|
||||
// as metadata for crash report. Falls back to
|
||||
// CFBundleVersion if not specified.
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
|
||||
// been created that you can send to <vendor>")
|
||||
//
|
||||
// BREAKPAD_URL URL destination for reporting
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
|
||||
// in. By default, we use
|
||||
// ~/Library/Cache/Breakpad/<BREAKPAD_PRODUCT>
|
||||
// The path you specify here is tilde-expanded.
|
||||
//
|
||||
// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to
|
||||
// rewrite the upload parameters for a specific
|
||||
// server type. The currently valid values are
|
||||
// 'socorro' or 'google'. If you want to add
|
||||
// other types, see the function in
|
||||
// crash_report_sender.m that maps parameters to
|
||||
// URL parameters. Defaults to 'google'.
|
||||
//
|
||||
// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static
|
||||
// parameters that are uploaded to the
|
||||
// server. The parameters are sent as
|
||||
// is to the crash server. Their
|
||||
// content isn't added to the minidump
|
||||
// but pass as URL parameters when
|
||||
// uploading theminidump to the crash
|
||||
// server.
|
||||
//=============================================================================
|
||||
// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
|
||||
// required to have non-NULL values. By default, the BREAKPAD_PRODUCT
|
||||
// will be the CFBundleName and the BREAKPAD_VERSION will be the
|
||||
// CFBundleVersion when these keys are present in the bundle's
|
||||
// Info.plist, which is usually passed in to BreakpadCreate() as an
|
||||
// NSDictionary (you could also pass in another dictionary that had
|
||||
// the same keys configured). If the BREAKPAD_PRODUCT or
|
||||
// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will
|
||||
// fail. You have been warned.
|
||||
//
|
||||
// If you are running in a debugger, Breakpad will not install, unless the
|
||||
// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
|
||||
//
|
||||
//=============================================================================
|
||||
// The following are NOT user-supplied but are documented here for
|
||||
// completeness. They are calculated by Breakpad during initialization &
|
||||
// crash-dump generation, or entered in by the user.
|
||||
//
|
||||
// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the
|
||||
// process started
|
||||
//
|
||||
// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
|
||||
// process crashed.
|
||||
//
|
||||
// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
|
||||
// has been running. This parameter is not
|
||||
// set until the crash-dump-generation phase.
|
||||
//
|
||||
// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad
|
||||
// internally, because Breakpad uses
|
||||
// the same dictionary internally to
|
||||
// track both its internal
|
||||
// configuration parameters and
|
||||
// parameters meant to be uploaded
|
||||
// to the server. This string is
|
||||
// used internally by Breakpad to
|
||||
// prefix user-supplied parameter
|
||||
// names so those can be sent to the
|
||||
// server without leaking Breakpad's
|
||||
// internal values.
|
||||
|
||||
// Returns a new BreakpadRef object on success, NULL otherwise.
|
||||
BreakpadRef BreakpadCreate(NSDictionary *parameters);
|
||||
|
||||
// Uninstall and release the data associated with |ref|.
|
||||
void BreakpadRelease(BreakpadRef ref);
|
||||
|
||||
// User defined key and value string storage. Generally this is used
|
||||
// to configure Breakpad's internal operation, such as whether the
|
||||
// crash_sender should prompt the user, or the filesystem location for
|
||||
// the minidump file. See Breakpad.h for some parameters that can be
|
||||
// set. Anything longer than 255 bytes will be truncated. Note that
|
||||
// the string is converted to UTF8 before truncation, so any multibyte
|
||||
// character that straddles the 255(256 - 1 for terminator) byte limit
|
||||
// will be mangled.
|
||||
//
|
||||
// A maximum number of 64 key/value pairs are supported. An assert()
|
||||
// will fire if more than this number are set. Unfortunately, right
|
||||
// now, the same dictionary is used for both Breakpad's parameters AND
|
||||
// the Upload parameters.
|
||||
//
|
||||
// TODO (nealsid): Investigate how necessary this is if we don't
|
||||
// automatically upload parameters to the server anymore.
|
||||
// TODO (nealsid): separate server parameter dictionary from the
|
||||
// dictionary used to configure Breakpad, and document limits for each
|
||||
// independently.
|
||||
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
|
||||
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
|
||||
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
|
||||
|
||||
// You can use this method to specify parameters that will be uploaded
|
||||
// to the crash server. They will be automatically encoded as
|
||||
// necessary. Note that as mentioned above there are limits on both
|
||||
// the number of keys and their length.
|
||||
void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
|
||||
NSString *value);
|
||||
|
||||
// This method will remove a previously-added parameter from the
|
||||
// upload parameter set.
|
||||
void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
|
||||
|
||||
// Method to handle uploading data to the server
|
||||
|
||||
// Returns the number of crash reports waiting to send to the server.
|
||||
int BreakpadGetCrashReportCount(BreakpadRef ref);
|
||||
|
||||
// Returns the next upload configuration. The report file is deleted.
|
||||
NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref);
|
||||
|
||||
// Upload next report to the server.
|
||||
void BreakpadUploadNextReport(BreakpadRef ref);
|
||||
|
||||
// Upload next report to the server.
|
||||
// |server_parameters| is additional server parameters to send.
|
||||
void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
|
||||
NSDictionary *server_parameters);
|
||||
|
||||
// Upload a report to the server.
|
||||
// |server_parameters| is additional server parameters to send.
|
||||
// |configuration| is the configuration of the breakpad report to send.
|
||||
void BreakpadUploadReportWithParametersAndConfiguration(
|
||||
BreakpadRef ref,
|
||||
NSDictionary *server_parameters,
|
||||
NSDictionary *configuration);
|
||||
|
||||
// Handles the network response of a breakpad upload. This function is needed if
|
||||
// the actual upload is done by the Breakpad client.
|
||||
// |configuration| is the configuration of the upload. It must contain the same
|
||||
// fields as the configuration passed to
|
||||
// BreakpadUploadReportWithParametersAndConfiguration.
|
||||
// |data| and |error| contain the network response.
|
||||
void BreakpadHandleNetworkResponse(BreakpadRef ref,
|
||||
NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error);
|
||||
|
||||
// Upload a file to the server. |data| is the content of the file to sent.
|
||||
// |server_parameters| is additional server parameters to send.
|
||||
void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters);
|
||||
|
||||
// Generate a breakpad minidump and configuration file in the dump directory.
|
||||
// The report will be available for uploading. The paths of the created files
|
||||
// are returned in the dictionary. |server_parameters| is additional server
|
||||
// parameters to add in the config file.
|
||||
NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
|
||||
NSDictionary *server_parameters);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
916
src/client/ios/Breakpad.mm
Normal file
916
src/client/ios/Breakpad.mm
Normal file
@ -0,0 +1,916 @@
|
||||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
|
||||
|
||||
#import "client/ios/Breakpad.h"
|
||||
|
||||
#include <assert.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#import "client/ios/handler/ios_exception_minidump_generator.h"
|
||||
#import "client/mac/crash_generation/ConfigFile.h"
|
||||
#import "client/mac/handler/exception_handler.h"
|
||||
#import "client/mac/handler/minidump_generator.h"
|
||||
#import "client/mac/sender/uploader.h"
|
||||
#import "client/mac/handler/protected_memory_allocator.h"
|
||||
#import "common/simple_string_dictionary.h"
|
||||
|
||||
#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions))
|
||||
// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
|
||||
// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
|
||||
// exceptions disabled even when other C++ libraries are used. #undef the try
|
||||
// and catch macros first in case libstdc++ is in use and has already provided
|
||||
// its own definitions.
|
||||
#undef try
|
||||
#define try if (true)
|
||||
#undef catch
|
||||
#define catch(X) if (false)
|
||||
#endif // __EXCEPTIONS
|
||||
|
||||
using google_breakpad::ConfigFile;
|
||||
using google_breakpad::EnsureDirectoryPathExists;
|
||||
using google_breakpad::SimpleStringDictionary;
|
||||
|
||||
//=============================================================================
|
||||
// We want any memory allocations which are used by breakpad during the
|
||||
// exception handling process (after a crash has happened) to be read-only
|
||||
// to prevent them from being smashed before a crash occurs. Unfortunately
|
||||
// we cannot protect against smashes to our exception handling thread's
|
||||
// stack.
|
||||
//
|
||||
// NOTE: Any memory allocations which are not used during the exception
|
||||
// handling process may be allocated in the normal ways.
|
||||
//
|
||||
// The ProtectedMemoryAllocator class provides an Allocate() method which
|
||||
// we'll using in conjunction with placement operator new() to control
|
||||
// allocation of C++ objects. Note that we don't use operator delete()
|
||||
// but instead call the objects destructor directly: object->~ClassName();
|
||||
//
|
||||
ProtectedMemoryAllocator *gMasterAllocator = NULL;
|
||||
ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
|
||||
ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
|
||||
|
||||
// Mutex for thread-safe access to the key/value dictionary used by breakpad.
|
||||
// It's a global instead of an instance variable of Breakpad
|
||||
// since it can't live in a protected memory area.
|
||||
pthread_mutex_t gDictionaryMutex;
|
||||
|
||||
//=============================================================================
|
||||
// Stack-based object for thread-safe access to a memory-protected region.
|
||||
// It's assumed that normally the memory block (allocated by the allocator)
|
||||
// is protected (read-only). Creating a stack-based instance of
|
||||
// ProtectedMemoryLocker will unprotect this block after taking the lock.
|
||||
// Its destructor will first re-protect the memory then release the lock.
|
||||
class ProtectedMemoryLocker {
|
||||
public:
|
||||
ProtectedMemoryLocker(pthread_mutex_t *mutex,
|
||||
ProtectedMemoryAllocator *allocator)
|
||||
: mutex_(mutex),
|
||||
allocator_(allocator) {
|
||||
// Lock the mutex
|
||||
__attribute__((unused)) int rv = pthread_mutex_lock(mutex_);
|
||||
assert(rv == 0);
|
||||
|
||||
// Unprotect the memory
|
||||
allocator_->Unprotect();
|
||||
}
|
||||
|
||||
~ProtectedMemoryLocker() {
|
||||
// First protect the memory
|
||||
allocator_->Protect();
|
||||
|
||||
// Then unlock the mutex
|
||||
__attribute__((unused)) int rv = pthread_mutex_unlock(mutex_);
|
||||
assert(rv == 0);
|
||||
};
|
||||
|
||||
private:
|
||||
ProtectedMemoryLocker();
|
||||
ProtectedMemoryLocker(const ProtectedMemoryLocker&);
|
||||
ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
|
||||
|
||||
pthread_mutex_t *mutex_;
|
||||
ProtectedMemoryAllocator *allocator_;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
class Breakpad {
|
||||
public:
|
||||
// factory method
|
||||
static Breakpad *Create(NSDictionary *parameters) {
|
||||
// Allocate from our special allocation pool
|
||||
Breakpad *breakpad =
|
||||
new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
|
||||
Breakpad();
|
||||
|
||||
if (!breakpad)
|
||||
return NULL;
|
||||
|
||||
if (!breakpad->Initialize(parameters)) {
|
||||
// Don't use operator delete() here since we allocated from special pool
|
||||
breakpad->~Breakpad();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return breakpad;
|
||||
}
|
||||
|
||||
~Breakpad();
|
||||
|
||||
void SetKeyValue(NSString *key, NSString *value);
|
||||
NSString *KeyValue(NSString *key);
|
||||
void RemoveKeyValue(NSString *key);
|
||||
NSArray *CrashReportsToUpload();
|
||||
NSString *NextCrashReportToUpload();
|
||||
NSDictionary *NextCrashReportConfiguration();
|
||||
void UploadNextReport(NSDictionary *server_parameters);
|
||||
void UploadReportWithConfiguration(NSDictionary *configuration,
|
||||
NSDictionary *server_parameters);
|
||||
void UploadData(NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters);
|
||||
void HandleNetworkResponse(NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error);
|
||||
NSDictionary *GenerateReport(NSDictionary *server_parameters);
|
||||
|
||||
private:
|
||||
Breakpad()
|
||||
: handler_(NULL),
|
||||
config_params_(NULL) {}
|
||||
|
||||
bool Initialize(NSDictionary *parameters);
|
||||
|
||||
bool ExtractParameters(NSDictionary *parameters);
|
||||
|
||||
// Dispatches to HandleMinidump()
|
||||
static bool HandleMinidumpCallback(const char *dump_dir,
|
||||
const char *minidump_id,
|
||||
void *context, bool succeeded);
|
||||
|
||||
bool HandleMinidump(const char *dump_dir,
|
||||
const char *minidump_id);
|
||||
|
||||
// NSException handler
|
||||
static void UncaughtExceptionHandler(NSException *exception);
|
||||
|
||||
// Handle an uncaught NSException.
|
||||
void HandleUncaughtException(NSException *exception);
|
||||
|
||||
// Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
|
||||
// MachineExceptions.h, we have to explicitly name the handler.
|
||||
google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
|
||||
|
||||
SimpleStringDictionary *config_params_; // Create parameters (STRONG)
|
||||
|
||||
ConfigFile config_file_;
|
||||
|
||||
// A static reference to the current Breakpad instance. Used for handling
|
||||
// NSException.
|
||||
static Breakpad *current_breakpad_;
|
||||
};
|
||||
|
||||
Breakpad *Breakpad::current_breakpad_ = NULL;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Helper functions
|
||||
|
||||
//=============================================================================
|
||||
// Helper functions
|
||||
|
||||
//=============================================================================
|
||||
static BOOL IsDebuggerActive() {
|
||||
BOOL result = NO;
|
||||
NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
// We check both defaults and the environment variable here
|
||||
|
||||
BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
|
||||
|
||||
if (!ignoreDebugger) {
|
||||
char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
|
||||
ignoreDebugger =
|
||||
(ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
|
||||
}
|
||||
|
||||
if (!ignoreDebugger) {
|
||||
pid_t pid = getpid();
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
||||
int mibSize = sizeof(mib) / sizeof(int);
|
||||
size_t actualSize;
|
||||
|
||||
if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
|
||||
struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
|
||||
|
||||
if (info) {
|
||||
// This comes from looking at the Darwin xnu Kernel
|
||||
if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
|
||||
result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
|
||||
|
||||
free(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
|
||||
const char *minidump_id,
|
||||
void *context, bool succeeded) {
|
||||
Breakpad *breakpad = (Breakpad *)context;
|
||||
|
||||
// If our context is damaged or something, just return false to indicate that
|
||||
// the handler should continue without us.
|
||||
if (!breakpad || !succeeded)
|
||||
return false;
|
||||
|
||||
return breakpad->HandleMinidump(dump_dir, minidump_id);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UncaughtExceptionHandler(NSException *exception) {
|
||||
NSSetUncaughtExceptionHandler(NULL);
|
||||
if (current_breakpad_) {
|
||||
current_breakpad_->HandleUncaughtException(exception);
|
||||
BreakpadRelease(current_breakpad_);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
#pragma mark -
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::Initialize(NSDictionary *parameters) {
|
||||
// Initialize
|
||||
current_breakpad_ = this;
|
||||
config_params_ = NULL;
|
||||
handler_ = NULL;
|
||||
|
||||
// Gather any user specified parameters
|
||||
if (!ExtractParameters(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for debugger
|
||||
if (IsDebuggerActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create the handler (allocating it in our special protected pool)
|
||||
handler_ =
|
||||
new (gBreakpadAllocator->Allocate(
|
||||
sizeof(google_breakpad::ExceptionHandler)))
|
||||
google_breakpad::ExceptionHandler(
|
||||
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
|
||||
0, &HandleMinidumpCallback, this, true, 0);
|
||||
NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
Breakpad::~Breakpad() {
|
||||
NSSetUncaughtExceptionHandler(NULL);
|
||||
current_breakpad_ = NULL;
|
||||
// Note that we don't use operator delete() on these pointers,
|
||||
// since they were allocated by ProtectedMemoryAllocator objects.
|
||||
//
|
||||
if (config_params_) {
|
||||
config_params_->~SimpleStringDictionary();
|
||||
}
|
||||
|
||||
if (handler_)
|
||||
handler_->~ExceptionHandler();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
||||
NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
|
||||
NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||
NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
|
||||
NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
|
||||
NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
|
||||
NSString *vendor =
|
||||
[parameters objectForKey:@BREAKPAD_VENDOR];
|
||||
// We check both parameters and the environment variable here.
|
||||
char *envVarDumpSubdirectory = getenv(BREAKPAD_DUMP_DIRECTORY);
|
||||
NSString *dumpSubdirectory = envVarDumpSubdirectory ?
|
||||
[NSString stringWithUTF8String:envVarDumpSubdirectory] :
|
||||
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
|
||||
|
||||
NSDictionary *serverParameters =
|
||||
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
|
||||
|
||||
if (!product)
|
||||
product = [parameters objectForKey:@"CFBundleName"];
|
||||
|
||||
if (!display) {
|
||||
display = [parameters objectForKey:@"CFBundleDisplayName"];
|
||||
if (!display) {
|
||||
display = product;
|
||||
}
|
||||
}
|
||||
|
||||
if (!version.length) // Default nil or empty string to CFBundleVersion
|
||||
version = [parameters objectForKey:@"CFBundleVersion"];
|
||||
|
||||
if (!vendor) {
|
||||
vendor = @"Vendor not specified";
|
||||
}
|
||||
|
||||
if (!dumpSubdirectory) {
|
||||
NSString *cachePath =
|
||||
[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
|
||||
NSUserDomainMask,
|
||||
YES)
|
||||
objectAtIndex:0];
|
||||
dumpSubdirectory =
|
||||
[cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
|
||||
|
||||
EnsureDirectoryPathExists(dumpSubdirectory);
|
||||
}
|
||||
|
||||
// The product, version, and URL are required values.
|
||||
if (![product length]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (![version length]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (![urlStr length]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config_params_ =
|
||||
new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
|
||||
SimpleStringDictionary();
|
||||
|
||||
SimpleStringDictionary &dictionary = *config_params_;
|
||||
|
||||
dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
|
||||
[dumpSubdirectory UTF8String]);
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
char timeStartedString[32];
|
||||
sprintf(timeStartedString, "%zd", tv.tv_sec);
|
||||
dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
|
||||
|
||||
if (serverParameters) {
|
||||
// For each key-value pair, call BreakpadAddUploadParameter()
|
||||
NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
|
||||
NSString *aParameter;
|
||||
while ((aParameter = [keyEnumerator nextObject])) {
|
||||
BreakpadAddUploadParameter(this, aParameter,
|
||||
[serverParameters objectForKey:aParameter]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::SetKeyValue(NSString *key, NSString *value) {
|
||||
// We allow nil values. This is the same as removing the keyvalue.
|
||||
if (!config_params_ || !key)
|
||||
return;
|
||||
|
||||
config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSString *Breakpad::KeyValue(NSString *key) {
|
||||
if (!config_params_ || !key)
|
||||
return nil;
|
||||
|
||||
const char *value = config_params_->GetValueForKey([key UTF8String]);
|
||||
return value ? [NSString stringWithUTF8String:value] : nil;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::RemoveKeyValue(NSString *key) {
|
||||
if (!config_params_ || !key) return;
|
||||
|
||||
config_params_->RemoveKey([key UTF8String]);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSArray *Breakpad::CrashReportsToUpload() {
|
||||
NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
|
||||
if (!directory)
|
||||
return nil;
|
||||
NSArray *dirContents = [[NSFileManager defaultManager]
|
||||
contentsOfDirectoryAtPath:directory error:nil];
|
||||
NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
|
||||
predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
|
||||
return configs;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSString *Breakpad::NextCrashReportToUpload() {
|
||||
NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
|
||||
if (!directory)
|
||||
return nil;
|
||||
NSString *config = [CrashReportsToUpload() lastObject];
|
||||
if (!config)
|
||||
return nil;
|
||||
return [NSString stringWithFormat:@"%@/%@", directory, config];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *Breakpad::NextCrashReportConfiguration() {
|
||||
return [Uploader readConfigurationDataFromFile:NextCrashReportToUpload()];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::HandleNetworkResponse(NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
Uploader *uploader = [[[Uploader alloc]
|
||||
initWithConfig:configuration] autorelease];
|
||||
[uploader handleNetworkResponse:data withError:error];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration,
|
||||
NSDictionary *server_parameters) {
|
||||
Uploader *uploader = [[[Uploader alloc]
|
||||
initWithConfig:configuration] autorelease];
|
||||
if (!uploader)
|
||||
return;
|
||||
for (NSString *key in server_parameters) {
|
||||
[uploader addServerParameter:[server_parameters objectForKey:key]
|
||||
forKey:key];
|
||||
}
|
||||
[uploader report];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
|
||||
NSDictionary *configuration = NextCrashReportConfiguration();
|
||||
if (configuration) {
|
||||
return UploadReportWithConfiguration(configuration, server_parameters);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UploadData(NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters) {
|
||||
NSMutableDictionary *config = [NSMutableDictionary dictionary];
|
||||
|
||||
SimpleStringDictionary::Iterator it(*config_params_);
|
||||
while (const SimpleStringDictionary::Entry *next = it.Next()) {
|
||||
[config setValue:[NSString stringWithUTF8String:next->value]
|
||||
forKey:[NSString stringWithUTF8String:next->key]];
|
||||
}
|
||||
|
||||
Uploader *uploader =
|
||||
[[[Uploader alloc] initWithConfig:config] autorelease];
|
||||
for (NSString *key in server_parameters) {
|
||||
[uploader addServerParameter:[server_parameters objectForKey:key]
|
||||
forKey:key];
|
||||
}
|
||||
[uploader uploadData:data name:name];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
|
||||
NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
|
||||
if (!dumpDirAsNSString)
|
||||
return nil;
|
||||
const char *dumpDir = [dumpDirAsNSString UTF8String];
|
||||
|
||||
google_breakpad::MinidumpGenerator generator(mach_task_self(),
|
||||
MACH_PORT_NULL);
|
||||
std::string dumpId;
|
||||
std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
|
||||
bool success = generator.Write(dumpFilename.c_str());
|
||||
if (!success)
|
||||
return nil;
|
||||
|
||||
SimpleStringDictionary params = *config_params_;
|
||||
for (NSString *key in server_parameters) {
|
||||
params.SetKeyValue([key UTF8String],
|
||||
[[server_parameters objectForKey:key] UTF8String]);
|
||||
}
|
||||
ConfigFile config_file;
|
||||
config_file.WriteFile(dumpDir, ¶ms, dumpDir, dumpId.c_str());
|
||||
|
||||
// Handle results.
|
||||
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
||||
NSString *dumpFullPath = [NSString stringWithUTF8String:dumpFilename.c_str()];
|
||||
[result setValue:dumpFullPath
|
||||
forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
|
||||
[result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
|
||||
forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::HandleMinidump(const char *dump_dir,
|
||||
const char *minidump_id) {
|
||||
config_file_.WriteFile(dump_dir,
|
||||
config_params_,
|
||||
dump_dir,
|
||||
minidump_id);
|
||||
|
||||
// Return true here to indicate that we've processed things as much as we
|
||||
// want.
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::HandleUncaughtException(NSException *exception) {
|
||||
// Generate the minidump.
|
||||
google_breakpad::IosExceptionMinidumpGenerator generator(exception);
|
||||
const char *minidump_path =
|
||||
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
|
||||
std::string minidump_id;
|
||||
std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
|
||||
&minidump_id);
|
||||
generator.Write(minidump_filename.c_str());
|
||||
|
||||
// Copy the config params and our custom parameter. This is necessary for 2
|
||||
// reasons:
|
||||
// 1- config_params_ is protected.
|
||||
// 2- If the application crash while trying to handle this exception, a usual
|
||||
// report will be generated. This report must not contain these special
|
||||
// keys.
|
||||
SimpleStringDictionary params = *config_params_;
|
||||
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
|
||||
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
|
||||
[[exception name] UTF8String]);
|
||||
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
|
||||
[[exception reason] UTF8String]);
|
||||
|
||||
// And finally write the config file.
|
||||
ConfigFile config_file;
|
||||
config_file.WriteFile(minidump_path,
|
||||
¶ms,
|
||||
minidump_path,
|
||||
minidump_id.c_str());
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Public API
|
||||
|
||||
//=============================================================================
|
||||
BreakpadRef BreakpadCreate(NSDictionary *parameters) {
|
||||
try {
|
||||
// This is confusing. Our two main allocators for breakpad memory are:
|
||||
// - gKeyValueAllocator for the key/value memory
|
||||
// - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
|
||||
// breakpad allocations which are accessed at exception handling time.
|
||||
//
|
||||
// But in order to avoid these two allocators themselves from being smashed,
|
||||
// we'll protect them as well by allocating them with gMasterAllocator.
|
||||
//
|
||||
// gMasterAllocator itself will NOT be protected, but this doesn't matter,
|
||||
// since once it does its allocations and locks the memory, smashes to
|
||||
// itself don't affect anything we care about.
|
||||
gMasterAllocator =
|
||||
new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
|
||||
|
||||
gKeyValueAllocator =
|
||||
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
|
||||
ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
|
||||
|
||||
// Create a mutex for use in accessing the SimpleStringDictionary
|
||||
int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
|
||||
if (mutexResult == 0) {
|
||||
|
||||
// With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
|
||||
// Let's round up to the nearest page size.
|
||||
//
|
||||
int breakpad_pool_size = 4096;
|
||||
|
||||
/*
|
||||
sizeof(Breakpad)
|
||||
+ sizeof(google_breakpad::ExceptionHandler)
|
||||
+ sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
|
||||
*/
|
||||
|
||||
gBreakpadAllocator =
|
||||
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
|
||||
ProtectedMemoryAllocator(breakpad_pool_size);
|
||||
|
||||
// Stack-based autorelease pool for Breakpad::Create() obj-c code.
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
Breakpad *breakpad = Breakpad::Create(parameters);
|
||||
|
||||
if (breakpad) {
|
||||
// Make read-only to protect against memory smashers
|
||||
gMasterAllocator->Protect();
|
||||
gKeyValueAllocator->Protect();
|
||||
gBreakpadAllocator->Protect();
|
||||
// Can uncomment this line to figure out how much space was actually
|
||||
// allocated using this allocator
|
||||
// printf("gBreakpadAllocator allocated size = %d\n",
|
||||
// gBreakpadAllocator->GetAllocatedSize() );
|
||||
[pool release];
|
||||
return (BreakpadRef)breakpad;
|
||||
}
|
||||
|
||||
[pool release];
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadCreate() : error\n");
|
||||
}
|
||||
|
||||
if (gKeyValueAllocator) {
|
||||
gKeyValueAllocator->~ProtectedMemoryAllocator();
|
||||
gKeyValueAllocator = NULL;
|
||||
}
|
||||
|
||||
if (gBreakpadAllocator) {
|
||||
gBreakpadAllocator->~ProtectedMemoryAllocator();
|
||||
gBreakpadAllocator = NULL;
|
||||
}
|
||||
|
||||
delete gMasterAllocator;
|
||||
gMasterAllocator = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadRelease(BreakpadRef ref) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (gMasterAllocator) {
|
||||
gMasterAllocator->Unprotect();
|
||||
gKeyValueAllocator->Unprotect();
|
||||
gBreakpadAllocator->Unprotect();
|
||||
|
||||
breakpad->~Breakpad();
|
||||
|
||||
// Unfortunately, it's not possible to deallocate this stuff
|
||||
// because the exception handling thread is still finishing up
|
||||
// asynchronously at this point... OK, it could be done with
|
||||
// locks, etc. But since BreakpadRelease() should usually only
|
||||
// be called right before the process exits, it's not worth
|
||||
// deallocating this stuff.
|
||||
#if 0
|
||||
gKeyValueAllocator->~ProtectedMemoryAllocator();
|
||||
gBreakpadAllocator->~ProtectedMemoryAllocator();
|
||||
delete gMasterAllocator;
|
||||
|
||||
gMasterAllocator = NULL;
|
||||
gKeyValueAllocator = NULL;
|
||||
gBreakpadAllocator = NULL;
|
||||
#endif
|
||||
|
||||
pthread_mutex_destroy(&gDictionaryMutex);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadRelease() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
breakpad->SetKeyValue(key, value);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpadAddUploadParameter(BreakpadRef ref,
|
||||
NSString *key,
|
||||
NSString *value) {
|
||||
// The only difference, internally, between an upload parameter and
|
||||
// a key value one that is set with BreakpadSetKeyValue is that we
|
||||
// prepend the keyname with a special prefix. This informs the
|
||||
// crash sender that the parameter should be sent along with the
|
||||
// POST of the crash dump upload.
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
|
||||
stringByAppendingString:key];
|
||||
breakpad->SetKeyValue(prefixedKey, value);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpadRemoveUploadParameter(BreakpadRef ref,
|
||||
NSString *key) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
|
||||
@BREAKPAD_SERVER_PARAMETER_PREFIX, key];
|
||||
breakpad->RemoveKeyValue(prefixedKey);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
//=============================================================================
|
||||
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
|
||||
NSString *value = nil;
|
||||
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (!breakpad || !key || !gKeyValueAllocator)
|
||||
return nil;
|
||||
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
value = breakpad->KeyValue(key);
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadKeyValue() : error\n");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
breakpad->RemoveKeyValue(key);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int BreakpadGetCrashReportCount(BreakpadRef ref) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad) {
|
||||
return static_cast<int>([breakpad->CrashReportsToUpload() count]);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadGetCrashReportCount() : error\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadNextReport(BreakpadRef ref) {
|
||||
BreakpadUploadNextReportWithParameters(ref, nil);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (breakpad)
|
||||
return breakpad->NextCrashReportConfiguration();
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadGetNextReportConfiguration() : error\n");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadReportWithParametersAndConfiguration(
|
||||
BreakpadRef ref,
|
||||
NSDictionary *server_parameters,
|
||||
NSDictionary *configuration) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (!breakpad || !configuration)
|
||||
return;
|
||||
breakpad->UploadReportWithConfiguration(configuration, server_parameters);
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr,
|
||||
"BreakpadUploadReportWithParametersAndConfiguration() : error\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
|
||||
NSDictionary *server_parameters) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (!breakpad)
|
||||
return;
|
||||
NSDictionary *configuration = breakpad->NextCrashReportConfiguration();
|
||||
if (!configuration)
|
||||
return;
|
||||
return BreakpadUploadReportWithParametersAndConfiguration(ref,
|
||||
server_parameters,
|
||||
configuration);
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpadHandleNetworkResponse(BreakpadRef ref,
|
||||
NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (breakpad && configuration)
|
||||
breakpad->HandleNetworkResponse(configuration,data, error);
|
||||
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadHandleNetworkResponse() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad) {
|
||||
breakpad->UploadData(data, name, server_parameters);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadUploadData() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
|
||||
NSDictionary *server_parameters) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad) {
|
||||
return breakpad->GenerateReport(server_parameters);
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadGenerateReport() : error\n");
|
||||
return nil;
|
||||
}
|
||||
}
|
578
src/client/ios/Breakpad.xcodeproj/project.pbxproj
Normal file
578
src/client/ios/Breakpad.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,578 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
14569321182CE29F0029C465 /* ucontext_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569320182CE29F0029C465 /* ucontext_compat.h */; };
|
||||
14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569322182CE2C10029C465 /* mach_vm_compat.h */; };
|
||||
16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */; };
|
||||
16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */; };
|
||||
16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C968147D4A4200776EAD /* BreakpadDefines.h */; };
|
||||
16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C96A147D4A4200776EAD /* Breakpad.h */; };
|
||||
16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7C96B147D4A4200776EAD /* Breakpad.mm */; };
|
||||
16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CB9E147D4A4300776EAD /* ConfigFile.h */; };
|
||||
16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */; };
|
||||
16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */; };
|
||||
16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */; };
|
||||
16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */; };
|
||||
16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB0147D4A4300776EAD /* dynamic_images.h */; };
|
||||
16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB1147D4A4300776EAD /* exception_handler.cc */; };
|
||||
16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB2147D4A4300776EAD /* exception_handler.h */; };
|
||||
16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */; };
|
||||
16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB5147D4A4300776EAD /* minidump_generator.h */; };
|
||||
16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */; };
|
||||
16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */; };
|
||||
16C7CE08147D4A4300776EAD /* uploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBEA147D4A4300776EAD /* uploader.h */; };
|
||||
16C7CE09147D4A4300776EAD /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBEB147D4A4300776EAD /* uploader.mm */; };
|
||||
16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */; };
|
||||
16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */; };
|
||||
16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */; };
|
||||
16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC4A147D4A4300776EAD /* convert_UTF.c */; };
|
||||
16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC4B147D4A4300776EAD /* convert_UTF.h */; };
|
||||
16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC88147D4A4300776EAD /* GTMLogger.h */; };
|
||||
16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC89147D4A4300776EAD /* GTMLogger.m */; };
|
||||
16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */; };
|
||||
16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */; };
|
||||
16C7CE83147D4A4300776EAD /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC93147D4A4300776EAD /* file_id.cc */; };
|
||||
16C7CE84147D4A4300776EAD /* file_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC94147D4A4300776EAD /* file_id.h */; };
|
||||
16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC95147D4A4300776EAD /* macho_id.cc */; };
|
||||
16C7CE86147D4A4300776EAD /* macho_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC96147D4A4300776EAD /* macho_id.h */; };
|
||||
16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */; };
|
||||
16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9B147D4A4300776EAD /* macho_utilities.h */; };
|
||||
16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9C147D4A4300776EAD /* macho_walker.cc */; };
|
||||
16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9D147D4A4300776EAD /* macho_walker.h */; };
|
||||
16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9F147D4A4300776EAD /* string_utilities.cc */; };
|
||||
16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA0147D4A4300776EAD /* string_utilities.h */; };
|
||||
16C7CE93147D4A4300776EAD /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCA4147D4A4300776EAD /* md5.cc */; };
|
||||
16C7CE94147D4A4300776EAD /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA5147D4A4300776EAD /* md5.h */; };
|
||||
16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCB9147D4A4300776EAD /* string_conversion.cc */; };
|
||||
16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCBA147D4A4300776EAD /* string_conversion.h */; };
|
||||
16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C92FAB150DF8330053D7BA /* BreakpadController.h */; };
|
||||
16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C92FAC150DF8330053D7BA /* BreakpadController.mm */; };
|
||||
1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */; };
|
||||
1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */; };
|
||||
AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */; };
|
||||
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
14569320182CE29F0029C465 /* ucontext_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ucontext_compat.h; sourceTree = "<group>"; };
|
||||
14569322182CE2C10029C465 /* mach_vm_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_vm_compat.h; sourceTree = "<group>"; };
|
||||
16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios_exception_minidump_generator.h; sourceTree = "<group>"; };
|
||||
16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios_exception_minidump_generator.mm; sourceTree = "<group>"; };
|
||||
16C7C968147D4A4200776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = "<group>"; };
|
||||
16C7C96A147D4A4200776EAD /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad.h; sourceTree = "<group>"; };
|
||||
16C7C96B147D4A4200776EAD /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Breakpad.mm; sourceTree = "<group>"; };
|
||||
16C7CB9E147D4A4300776EAD /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigFile.h; sourceTree = "<group>"; };
|
||||
16C7CB9F147D4A4300776EAD /* ConfigFile.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ConfigFile.mm; sourceTree = "<group>"; };
|
||||
16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
|
||||
16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = "<group>"; };
|
||||
16C7CBAF147D4A4300776EAD /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = "<group>"; };
|
||||
16C7CBB0147D4A4300776EAD /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = "<group>"; };
|
||||
16C7CBB1147D4A4300776EAD /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = "<group>"; };
|
||||
16C7CBB2147D4A4300776EAD /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = "<group>"; };
|
||||
16C7CBB4147D4A4300776EAD /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = "<group>"; };
|
||||
16C7CBB5147D4A4300776EAD /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = "<group>"; };
|
||||
16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protected_memory_allocator.cc; sourceTree = "<group>"; };
|
||||
16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = protected_memory_allocator.h; sourceTree = "<group>"; };
|
||||
16C7CBEA147D4A4300776EAD /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uploader.h; sourceTree = "<group>"; };
|
||||
16C7CBEB147D4A4300776EAD /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = uploader.mm; sourceTree = "<group>"; };
|
||||
16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "minidump_file_writer-inl.h"; sourceTree = "<group>"; };
|
||||
16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer.cc; sourceTree = "<group>"; };
|
||||
16C7CC06147D4A4300776EAD /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_file_writer.h; sourceTree = "<group>"; };
|
||||
16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
|
||||
16C7CC4A147D4A4300776EAD /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = convert_UTF.c; sourceTree = "<group>"; };
|
||||
16C7CC4B147D4A4300776EAD /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = convert_UTF.h; sourceTree = "<group>"; };
|
||||
16C7CC88147D4A4300776EAD /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLogger.h; sourceTree = "<group>"; };
|
||||
16C7CC89147D4A4300776EAD /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLogger.m; sourceTree = "<group>"; };
|
||||
16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPMultipartUpload.h; sourceTree = "<group>"; };
|
||||
16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPMultipartUpload.m; sourceTree = "<group>"; };
|
||||
16C7CC93147D4A4300776EAD /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_id.cc; sourceTree = "<group>"; };
|
||||
16C7CC94147D4A4300776EAD /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_id.h; sourceTree = "<group>"; };
|
||||
16C7CC95147D4A4300776EAD /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_id.cc; sourceTree = "<group>"; };
|
||||
16C7CC96147D4A4300776EAD /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_id.h; sourceTree = "<group>"; };
|
||||
16C7CC9A147D4A4300776EAD /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_utilities.cc; sourceTree = "<group>"; };
|
||||
16C7CC9B147D4A4300776EAD /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_utilities.h; sourceTree = "<group>"; };
|
||||
16C7CC9C147D4A4300776EAD /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_walker.cc; sourceTree = "<group>"; };
|
||||
16C7CC9D147D4A4300776EAD /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_walker.h; sourceTree = "<group>"; };
|
||||
16C7CC9F147D4A4300776EAD /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_utilities.cc; sourceTree = "<group>"; };
|
||||
16C7CCA0147D4A4300776EAD /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utilities.h; sourceTree = "<group>"; };
|
||||
16C7CCA4147D4A4300776EAD /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cc; sourceTree = "<group>"; };
|
||||
16C7CCA5147D4A4300776EAD /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
|
||||
16C7CCB9147D4A4300776EAD /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_conversion.cc; sourceTree = "<group>"; };
|
||||
16C7CCBA147D4A4300776EAD /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_conversion.h; sourceTree = "<group>"; };
|
||||
16C92FAB150DF8330053D7BA /* BreakpadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadController.h; sourceTree = "<group>"; };
|
||||
16C92FAC150DF8330053D7BA /* BreakpadController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BreakpadController.mm; sourceTree = "<group>"; };
|
||||
1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simple_string_dictionary.cc; sourceTree = "<group>"; };
|
||||
1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simple_string_dictionary.h; sourceTree = "<group>"; };
|
||||
AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad_Prefix.pch; sourceTree = SOURCE_ROOT; };
|
||||
AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
D2AAC07C0554694100DB518D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
034768DFFF38A50411DB9C8B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D2AAC07E0554694100DB518D /* libBreakpad.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0867D691FE84028FC02AAC07 /* Breakpad */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB77AEFE84172EC02AAC07 /* Classes */,
|
||||
32C88DFF0371C24200C91783 /* Other Sources */,
|
||||
0867D69AFE84028FC02AAC07 /* Frameworks */,
|
||||
034768DFFF38A50411DB9C8B /* Products */,
|
||||
);
|
||||
name = Breakpad;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0867D69AFE84028FC02AAC07 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AACBBE490F95108600F1A2B1 /* Foundation.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB77AEFE84172EC02AAC07 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C965147D4A4200776EAD /* client */,
|
||||
16C7CC47147D4A4300776EAD /* common */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16BFA66A14E195E9009704F8 /* handler */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */,
|
||||
16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */,
|
||||
);
|
||||
path = handler;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C965147D4A4200776EAD /* client */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C966147D4A4200776EAD /* apple */,
|
||||
16C7C969147D4A4200776EAD /* ios */,
|
||||
16C7C99E147D4A4200776EAD /* mac */,
|
||||
16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */,
|
||||
16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */,
|
||||
16C7CC06147D4A4300776EAD /* minidump_file_writer.h */,
|
||||
16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */,
|
||||
);
|
||||
name = client;
|
||||
path = ..;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
16C7C966147D4A4200776EAD /* apple */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C967147D4A4200776EAD /* Framework */,
|
||||
);
|
||||
path = apple;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C967147D4A4200776EAD /* Framework */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C968147D4A4200776EAD /* BreakpadDefines.h */,
|
||||
);
|
||||
path = Framework;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C969147D4A4200776EAD /* ios */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C92FAB150DF8330053D7BA /* BreakpadController.h */,
|
||||
16C92FAC150DF8330053D7BA /* BreakpadController.mm */,
|
||||
16BFA66A14E195E9009704F8 /* handler */,
|
||||
16C7C96A147D4A4200776EAD /* Breakpad.h */,
|
||||
16C7C96B147D4A4200776EAD /* Breakpad.mm */,
|
||||
);
|
||||
path = ios;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C99E147D4A4200776EAD /* mac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CB9D147D4A4300776EAD /* crash_generation */,
|
||||
16C7CBAA147D4A4300776EAD /* handler */,
|
||||
16C7CBC8147D4A4300776EAD /* sender */,
|
||||
);
|
||||
path = mac;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CB9D147D4A4300776EAD /* crash_generation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CB9E147D4A4300776EAD /* ConfigFile.h */,
|
||||
16C7CB9F147D4A4300776EAD /* ConfigFile.mm */,
|
||||
);
|
||||
path = crash_generation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CBAA147D4A4300776EAD /* handler */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */,
|
||||
16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */,
|
||||
16C7CBAF147D4A4300776EAD /* dynamic_images.cc */,
|
||||
16C7CBB0147D4A4300776EAD /* dynamic_images.h */,
|
||||
16C7CBB1147D4A4300776EAD /* exception_handler.cc */,
|
||||
16C7CBB2147D4A4300776EAD /* exception_handler.h */,
|
||||
14569322182CE2C10029C465 /* mach_vm_compat.h */,
|
||||
16C7CBB4147D4A4300776EAD /* minidump_generator.cc */,
|
||||
16C7CBB5147D4A4300776EAD /* minidump_generator.h */,
|
||||
16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */,
|
||||
16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */,
|
||||
14569320182CE29F0029C465 /* ucontext_compat.h */,
|
||||
);
|
||||
path = handler;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CBC8147D4A4300776EAD /* sender */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CBEA147D4A4300776EAD /* uploader.h */,
|
||||
16C7CBEB147D4A4300776EAD /* uploader.mm */,
|
||||
);
|
||||
path = sender;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CC47147D4A4300776EAD /* common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */,
|
||||
1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */,
|
||||
16C7CC4A147D4A4300776EAD /* convert_UTF.c */,
|
||||
16C7CC4B147D4A4300776EAD /* convert_UTF.h */,
|
||||
16C7CC82147D4A4300776EAD /* mac */,
|
||||
16C7CCA4147D4A4300776EAD /* md5.cc */,
|
||||
16C7CCA5147D4A4300776EAD /* md5.h */,
|
||||
16C7CCB9147D4A4300776EAD /* string_conversion.cc */,
|
||||
16C7CCBA147D4A4300776EAD /* string_conversion.h */,
|
||||
);
|
||||
name = common;
|
||||
path = ../../common;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
16C7CC82147D4A4300776EAD /* mac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CC88147D4A4300776EAD /* GTMLogger.h */,
|
||||
16C7CC89147D4A4300776EAD /* GTMLogger.m */,
|
||||
16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */,
|
||||
16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */,
|
||||
16C7CC93147D4A4300776EAD /* file_id.cc */,
|
||||
16C7CC94147D4A4300776EAD /* file_id.h */,
|
||||
16C7CC95147D4A4300776EAD /* macho_id.cc */,
|
||||
16C7CC96147D4A4300776EAD /* macho_id.h */,
|
||||
16C7CC9A147D4A4300776EAD /* macho_utilities.cc */,
|
||||
16C7CC9B147D4A4300776EAD /* macho_utilities.h */,
|
||||
16C7CC9C147D4A4300776EAD /* macho_walker.cc */,
|
||||
16C7CC9D147D4A4300776EAD /* macho_walker.h */,
|
||||
16C7CC9F147D4A4300776EAD /* string_utilities.cc */,
|
||||
16C7CCA0147D4A4300776EAD /* string_utilities.h */,
|
||||
);
|
||||
path = mac;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32C88DFF0371C24200C91783 /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */,
|
||||
);
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
D2AAC07A0554694100DB518D /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */,
|
||||
16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */,
|
||||
16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */,
|
||||
16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */,
|
||||
14569321182CE29F0029C465 /* ucontext_compat.h in Headers */,
|
||||
16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */,
|
||||
16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */,
|
||||
16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */,
|
||||
16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */,
|
||||
16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */,
|
||||
16C7CE08147D4A4300776EAD /* uploader.h in Headers */,
|
||||
16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */,
|
||||
16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */,
|
||||
16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */,
|
||||
16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */,
|
||||
16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */,
|
||||
16C7CE84147D4A4300776EAD /* file_id.h in Headers */,
|
||||
16C7CE86147D4A4300776EAD /* macho_id.h in Headers */,
|
||||
16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */,
|
||||
16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */,
|
||||
16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */,
|
||||
16C7CE94147D4A4300776EAD /* md5.h in Headers */,
|
||||
16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */,
|
||||
16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */,
|
||||
16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */,
|
||||
1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */,
|
||||
14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
D2AAC07D0554694100DB518D /* Breakpad */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */;
|
||||
buildPhases = (
|
||||
D2AAC07A0554694100DB518D /* Headers */,
|
||||
D2AAC07B0554694100DB518D /* Sources */,
|
||||
D2AAC07C0554694100DB518D /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Breakpad;
|
||||
productName = Breakpad;
|
||||
productReference = D2AAC07E0554694100DB518D /* libBreakpad.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
0867D690FE84028FC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
};
|
||||
buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
English,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
da,
|
||||
de,
|
||||
es,
|
||||
fr,
|
||||
it,
|
||||
ja,
|
||||
nl,
|
||||
no,
|
||||
sl,
|
||||
sv,
|
||||
tr,
|
||||
);
|
||||
mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */;
|
||||
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
D2AAC07D0554694100DB518D /* Breakpad */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
D2AAC07B0554694100DB518D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */,
|
||||
16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */,
|
||||
16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */,
|
||||
16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */,
|
||||
16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */,
|
||||
16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */,
|
||||
16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */,
|
||||
16C7CE09147D4A4300776EAD /* uploader.mm in Sources */,
|
||||
16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */,
|
||||
16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */,
|
||||
16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */,
|
||||
16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */,
|
||||
16C7CE83147D4A4300776EAD /* file_id.cc in Sources */,
|
||||
16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */,
|
||||
16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */,
|
||||
16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */,
|
||||
16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */,
|
||||
16C7CE93147D4A4300776EAD /* md5.cc in Sources */,
|
||||
16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */,
|
||||
16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */,
|
||||
16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */,
|
||||
1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB921F08733DC00010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DSTROOT = /tmp/Breakpad.dst;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
);
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
|
||||
INSTALL_PATH = /usr/local/lib;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
"\"$(SRCROOT)/../mac/gcov\"",
|
||||
);
|
||||
PRODUCT_NAME = Breakpad;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB922008733DC00010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
DSTROOT = /tmp/Breakpad.dst;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
);
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
|
||||
INSTALL_PATH = /usr/local/lib;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
"\"$(SRCROOT)/../mac/gcov\"",
|
||||
);
|
||||
PRODUCT_NAME = Breakpad;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB922308733DC00010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../,
|
||||
../../client/apple/Framework,
|
||||
../../common/mac,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
SDKROOT = iphoneos;
|
||||
WARNING_CFLAGS = "-Wundef";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB922408733DC00010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../,
|
||||
../../client/apple/Framework,
|
||||
../../common/mac,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
SDKROOT = iphoneos;
|
||||
WARNING_CFLAGS = "-Wundef";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB921F08733DC00010E9CD /* Debug */,
|
||||
1DEB922008733DC00010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB922308733DC00010E9CD /* Debug */,
|
||||
1DEB922408733DC00010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
|
||||
}
|
141
src/client/ios/BreakpadController.h
Normal file
141
src/client/ios/BreakpadController.h
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
|
||||
#define CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "client/ios/Breakpad.h"
|
||||
|
||||
// This class is used to offer a higher level API around BreakpadRef. It
|
||||
// configures it, ensures thread-safety, and sends crash reports back to the
|
||||
// collecting server. By default, no crash reports are sent, the user must call
|
||||
// |setUploadingEnabled:YES| to start the uploading.
|
||||
@interface BreakpadController : NSObject {
|
||||
@private
|
||||
// The dispatch queue that will own the breakpad reference.
|
||||
dispatch_queue_t queue_;
|
||||
|
||||
// Instance of Breakpad crash reporter. This is owned by the queue, but can
|
||||
// be created on the main thread at startup.
|
||||
BreakpadRef breakpadRef_;
|
||||
|
||||
// The dictionary that contains configuration for breakpad. Modifying it
|
||||
// should only happen when the controller is not started. The initial value
|
||||
// is the infoDictionary of the bundle of the application.
|
||||
NSMutableDictionary* configuration_;
|
||||
|
||||
// Whether or not crash reports should be uploaded.
|
||||
BOOL enableUploads_;
|
||||
|
||||
// Whether the controller has been started on the main thread. This is only
|
||||
// used to assert the initialization order is correct.
|
||||
BOOL started_;
|
||||
|
||||
// The interval to wait between two uploads. Value is 0 if no upload must be
|
||||
// done.
|
||||
int uploadIntervalInSeconds_;
|
||||
|
||||
// The dictionary that contains additional server parameters to send when
|
||||
// uploading crash reports.
|
||||
NSDictionary* uploadTimeParameters_;
|
||||
}
|
||||
|
||||
// Singleton.
|
||||
+ (BreakpadController*)sharedInstance;
|
||||
|
||||
// Update the controller configuration. Merges its old configuration with the
|
||||
// new one. Merge is done by replacing the old values by the new values.
|
||||
- (void)updateConfiguration:(NSDictionary*)configuration;
|
||||
|
||||
// Reset the controller configuration to its initial value, which is the
|
||||
// infoDictionary of the bundle of the application.
|
||||
- (void)resetConfiguration;
|
||||
|
||||
// Configure the URL to upload the report to. This must be called at least once
|
||||
// if the URL is not in the bundle information.
|
||||
- (void)setUploadingURL:(NSString*)url;
|
||||
|
||||
// Set the minimal interval between two uploads in seconds. This must be called
|
||||
// at least once if the interval is not in the bundle information. A value of 0
|
||||
// will prevent uploads.
|
||||
- (void)setUploadInterval:(int)intervalInSeconds;
|
||||
|
||||
// Set additional server parameters to send when uploading crash reports.
|
||||
- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters;
|
||||
|
||||
// Specify an upload parameter that will be added to the crash report when a
|
||||
// crash report is generated. See |BreakpadAddUploadParameter|.
|
||||
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key;
|
||||
|
||||
// Remove a previously-added parameter from the upload parameter set. See
|
||||
// |BreakpadRemoveUploadParameter|.
|
||||
- (void)removeUploadParameterForKey:(NSString*)key;
|
||||
|
||||
// Access the underlying BreakpadRef. This method is asynchronous, and will be
|
||||
// executed on the thread owning the BreakpadRef variable. Moreover, if the
|
||||
// controller is not started, the block will be called with a NULL parameter.
|
||||
- (void)withBreakpadRef:(void(^)(BreakpadRef))callback;
|
||||
|
||||
// Starts the BreakpadController by registering crash handlers. If
|
||||
// |onCurrentThread| is YES, all setup is done on the current thread, otherwise
|
||||
// it is done on a private queue.
|
||||
- (void)start:(BOOL)onCurrentThread;
|
||||
|
||||
// Unregisters the crash handlers.
|
||||
- (void)stop;
|
||||
|
||||
// Enables or disables uploading of crash reports, but does not stop the
|
||||
// BreakpadController.
|
||||
- (void)setUploadingEnabled:(BOOL)enabled;
|
||||
|
||||
// Check if there is currently a crash report to upload.
|
||||
- (void)hasReportToUpload:(void(^)(BOOL))callback;
|
||||
|
||||
// Get the number of crash reports waiting to upload.
|
||||
- (void)getCrashReportCount:(void(^)(int))callback;
|
||||
|
||||
// Get the next report to upload.
|
||||
// - If upload is disabled, callback will be called with (nil, -1).
|
||||
// - If a delay is to be waited before sending, callback will be called with
|
||||
// (nil, n), with n (> 0) being the number of seconds to wait.
|
||||
// - if no delay is needed, callback will be called with (0, configuration),
|
||||
// configuration being next report to upload, or nil if none is pending.
|
||||
- (void)getNextReportConfigurationOrSendDelay:
|
||||
(void(^)(NSDictionary*, int))callback;
|
||||
|
||||
// Sends synchronously the report specified by |configuration|. This method is
|
||||
// NOT thread safe and must be called from the breakpad thread.
|
||||
- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
|
||||
withBreakpadRef:(BreakpadRef)ref;
|
||||
|
||||
@end
|
||||
|
||||
#endif // CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
|
354
src/client/ios/BreakpadController.mm
Normal file
354
src/client/ios/BreakpadController.mm
Normal file
@ -0,0 +1,354 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import "BreakpadController.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#include <asl.h>
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <common/scoped_ptr.h>
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private Methods
|
||||
|
||||
@interface BreakpadController ()
|
||||
|
||||
// Init the singleton instance.
|
||||
- (id)initSingleton;
|
||||
|
||||
// Load a crash report and send it to the server.
|
||||
- (void)sendStoredCrashReports;
|
||||
|
||||
// Returns when a report can be sent. |-1| means never, |0| means that a report
|
||||
// can be sent immediately, a positive number is the number of seconds to wait
|
||||
// before being allowed to upload a report.
|
||||
- (int)sendDelay;
|
||||
|
||||
// Notifies that a report will be sent, and update the last sending time
|
||||
// accordingly.
|
||||
- (void)reportWillBeSent;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Anonymous namespace
|
||||
|
||||
namespace {
|
||||
|
||||
// The name of the user defaults key for the last submission to the crash
|
||||
// server.
|
||||
NSString* const kLastSubmission = @"com.google.Breakpad.LastSubmission";
|
||||
|
||||
// Returns a NSString describing the current platform.
|
||||
NSString* GetPlatform() {
|
||||
// Name of the system call for getting the platform.
|
||||
static const char kHwMachineSysctlName[] = "hw.machine";
|
||||
|
||||
NSString* result = nil;
|
||||
|
||||
size_t size = 0;
|
||||
if (sysctlbyname(kHwMachineSysctlName, NULL, &size, NULL, 0) || size == 0)
|
||||
return nil;
|
||||
google_breakpad::scoped_array<char> machine(new char[size]);
|
||||
if (sysctlbyname(kHwMachineSysctlName, machine.get(), &size, NULL, 0) == 0)
|
||||
result = [NSString stringWithUTF8String:machine.get()];
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark BreakpadController Implementation
|
||||
|
||||
@implementation BreakpadController
|
||||
|
||||
+ (BreakpadController*)sharedInstance {
|
||||
@synchronized(self) {
|
||||
static BreakpadController* sharedInstance_ =
|
||||
[[BreakpadController alloc] initSingleton];
|
||||
return sharedInstance_;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initSingleton {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
queue_ = dispatch_queue_create("com.google.BreakpadQueue", NULL);
|
||||
enableUploads_ = NO;
|
||||
started_ = NO;
|
||||
[self resetConfiguration];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Since this class is a singleton, this method is not expected to be called.
|
||||
- (void)dealloc {
|
||||
assert(!breakpadRef_);
|
||||
dispatch_release(queue_);
|
||||
[configuration_ release];
|
||||
[uploadTimeParameters_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)start:(BOOL)onCurrentThread {
|
||||
if (started_)
|
||||
return;
|
||||
started_ = YES;
|
||||
void(^startBlock)() = ^{
|
||||
assert(!breakpadRef_);
|
||||
breakpadRef_ = BreakpadCreate(configuration_);
|
||||
if (breakpadRef_) {
|
||||
BreakpadAddUploadParameter(breakpadRef_, @"platform", GetPlatform());
|
||||
}
|
||||
};
|
||||
if (onCurrentThread)
|
||||
startBlock();
|
||||
else
|
||||
dispatch_async(queue_, startBlock);
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
if (!started_)
|
||||
return;
|
||||
started_ = NO;
|
||||
dispatch_sync(queue_, ^{
|
||||
if (breakpadRef_) {
|
||||
BreakpadRelease(breakpadRef_);
|
||||
breakpadRef_ = NULL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This method must be called from the breakpad queue.
|
||||
- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
|
||||
withBreakpadRef:(BreakpadRef)ref {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"threadUnsafeSendReportWithConfiguration is called");
|
||||
if (breakpadRef_) {
|
||||
BreakpadUploadReportWithParametersAndConfiguration(breakpadRef_,
|
||||
uploadTimeParameters_,
|
||||
configuration);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUploadingEnabled:(BOOL)enabled {
|
||||
NSAssert(started_,
|
||||
@"The controller must be started before setUploadingEnabled is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (enabled == enableUploads_)
|
||||
return;
|
||||
if (enabled) {
|
||||
// Set this before calling doSendStoredCrashReport, because that
|
||||
// calls sendDelay, which in turn checks this flag.
|
||||
enableUploads_ = YES;
|
||||
[self sendStoredCrashReports];
|
||||
} else {
|
||||
enableUploads_ = NO;
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
||||
selector:@selector(sendStoredCrashReports)
|
||||
object:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateConfiguration:(NSDictionary*)configuration {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when updateConfiguration is called");
|
||||
[configuration_ addEntriesFromDictionary:configuration];
|
||||
NSString* uploadInterval =
|
||||
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
|
||||
if (uploadInterval)
|
||||
[self setUploadInterval:[uploadInterval intValue]];
|
||||
}
|
||||
|
||||
- (void)resetConfiguration {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when resetConfiguration is called");
|
||||
[configuration_ autorelease];
|
||||
configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy];
|
||||
NSString* uploadInterval =
|
||||
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
|
||||
[self setUploadInterval:[uploadInterval intValue]];
|
||||
[self setParametersToAddAtUploadTime:nil];
|
||||
}
|
||||
|
||||
- (void)setUploadingURL:(NSString*)url {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when setUploadingURL is called");
|
||||
[configuration_ setValue:url forKey:@BREAKPAD_URL];
|
||||
}
|
||||
|
||||
- (void)setUploadInterval:(int)intervalInSeconds {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when setUploadInterval is called");
|
||||
[configuration_ removeObjectForKey:@BREAKPAD_REPORT_INTERVAL];
|
||||
uploadIntervalInSeconds_ = intervalInSeconds;
|
||||
if (uploadIntervalInSeconds_ < 0)
|
||||
uploadIntervalInSeconds_ = 0;
|
||||
}
|
||||
|
||||
- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters {
|
||||
NSAssert(!started_, @"The controller must not be started when "
|
||||
"setParametersToAddAtUploadTime is called");
|
||||
[uploadTimeParameters_ autorelease];
|
||||
uploadTimeParameters_ = [uploadTimeParameters copy];
|
||||
}
|
||||
|
||||
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key {
|
||||
NSAssert(started_,
|
||||
@"The controller must be started before addUploadParameter is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (breakpadRef_)
|
||||
BreakpadAddUploadParameter(breakpadRef_, key, value);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeUploadParameterForKey:(NSString*)key {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"removeUploadParameterForKey is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (breakpadRef_)
|
||||
BreakpadRemoveUploadParameter(breakpadRef_, key);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)withBreakpadRef:(void(^)(BreakpadRef))callback {
|
||||
NSAssert(started_,
|
||||
@"The controller must be started before withBreakpadRef is called");
|
||||
dispatch_async(queue_, ^{
|
||||
callback(breakpadRef_);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)hasReportToUpload:(void(^)(BOOL))callback {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"hasReportToUpload is called");
|
||||
dispatch_async(queue_, ^{
|
||||
callback(breakpadRef_ && (BreakpadGetCrashReportCount(breakpadRef_) > 0));
|
||||
});
|
||||
}
|
||||
|
||||
- (void)getCrashReportCount:(void(^)(int))callback {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"getCrashReportCount is called");
|
||||
dispatch_async(queue_, ^{
|
||||
callback(breakpadRef_ ? BreakpadGetCrashReportCount(breakpadRef_) : 0);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)getNextReportConfigurationOrSendDelay:
|
||||
(void(^)(NSDictionary*, int))callback {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"getNextReportConfigurationOrSendDelay is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (!breakpadRef_) {
|
||||
callback(nil, -1);
|
||||
return;
|
||||
}
|
||||
int delay = [self sendDelay];
|
||||
if (delay != 0) {
|
||||
callback(nil, delay);
|
||||
return;
|
||||
}
|
||||
[self reportWillBeSent];
|
||||
callback(BreakpadGetNextReportConfiguration(breakpadRef_), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (int)sendDelay {
|
||||
if (!breakpadRef_ || uploadIntervalInSeconds_ <= 0 || !enableUploads_)
|
||||
return -1;
|
||||
|
||||
// To prevent overloading the crash server, crashes are not sent than one
|
||||
// report every |uploadIntervalInSeconds_|. A value in the user defaults is
|
||||
// used to keep the time of the last upload.
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
NSNumber *lastTimeNum = [userDefaults objectForKey:kLastSubmission];
|
||||
NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
|
||||
NSTimeInterval spanSeconds = CFAbsoluteTimeGetCurrent() - lastTime;
|
||||
|
||||
if (spanSeconds >= uploadIntervalInSeconds_)
|
||||
return 0;
|
||||
return uploadIntervalInSeconds_ - static_cast<int>(spanSeconds);
|
||||
}
|
||||
|
||||
- (void)reportWillBeSent {
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
[userDefaults setObject:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()]
|
||||
forKey:kLastSubmission];
|
||||
[userDefaults synchronize];
|
||||
}
|
||||
|
||||
- (void)sendStoredCrashReports {
|
||||
dispatch_async(queue_, ^{
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) == 0)
|
||||
return;
|
||||
|
||||
int timeToWait = [self sendDelay];
|
||||
|
||||
// Unable to ever send report.
|
||||
if (timeToWait == -1)
|
||||
return;
|
||||
|
||||
// A report can be sent now.
|
||||
if (timeToWait == 0) {
|
||||
[self reportWillBeSent];
|
||||
BreakpadUploadNextReportWithParameters(breakpadRef_,
|
||||
uploadTimeParameters_);
|
||||
|
||||
// If more reports must be sent, make sure this method is called again.
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) > 0)
|
||||
timeToWait = uploadIntervalInSeconds_;
|
||||
}
|
||||
|
||||
// A report must be sent later.
|
||||
if (timeToWait > 0) {
|
||||
// performSelector: doesn't work on queue_
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self performSelector:@selector(sendStoredCrashReports)
|
||||
withObject:nil
|
||||
afterDelay:timeToWait];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
7
src/client/ios/Breakpad_Prefix.pch
Normal file
7
src/client/ios/Breakpad_Prefix.pch
Normal file
@ -0,0 +1,7 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'CocoaTouchStaticLibrary' target in the 'CocoaTouchStaticLibrary' project.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
74
src/client/ios/handler/ios_exception_minidump_generator.h
Normal file
74
src/client/ios/handler/ios_exception_minidump_generator.h
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ios_exception_minidump_generator.h: Create a fake minidump from a
|
||||
// NSException.
|
||||
|
||||
#ifndef CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
|
||||
#define CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class IosExceptionMinidumpGenerator : public MinidumpGenerator {
|
||||
public:
|
||||
explicit IosExceptionMinidumpGenerator(NSException *exception);
|
||||
virtual ~IosExceptionMinidumpGenerator();
|
||||
|
||||
protected:
|
||||
virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
|
||||
virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
|
||||
|
||||
private:
|
||||
|
||||
// Get the crashing program counter from the exception.
|
||||
uintptr_t GetPCFromException();
|
||||
|
||||
// Get the crashing link register from the exception.
|
||||
uintptr_t GetLRFromException();
|
||||
|
||||
// Write a virtual thread context for the crashing site.
|
||||
bool WriteCrashingContext(MDLocationDescriptor *register_location);
|
||||
// Per-CPU implementations of the above method.
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
bool WriteCrashingContextARM(MDLocationDescriptor *register_location);
|
||||
#endif
|
||||
#ifdef HAS_ARM64_SUPPORT
|
||||
bool WriteCrashingContextARM64(MDLocationDescriptor *register_location);
|
||||
#endif
|
||||
|
||||
NSArray *return_addresses_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
|
210
src/client/ios/handler/ios_exception_minidump_generator.mm
Normal file
210
src/client/ios/handler/ios_exception_minidump_generator.mm
Normal file
@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/ios/handler/ios_exception_minidump_generator.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_cpu_arm.h"
|
||||
#include "google_breakpad/common/minidump_cpu_arm64.h"
|
||||
#include "google_breakpad/common/minidump_exception_mac.h"
|
||||
#include "client/minidump_file_writer-inl.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
|
||||
#if defined(HAS_ARM_SUPPORT) && defined(HAS_ARM64_SUPPORT)
|
||||
#error "This file should be compiled for only one architecture at a time"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
const int kExceptionType = EXC_SOFTWARE;
|
||||
const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION;
|
||||
|
||||
#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
|
||||
const uintptr_t kExpectedFinalFp = sizeof(uintptr_t);
|
||||
const uintptr_t kExpectedFinalSp = 0;
|
||||
|
||||
// Append the given value to the sp position of the stack represented
|
||||
// by memory.
|
||||
void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) {
|
||||
memcpy(memory + sp, &data, sizeof(data));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator(
|
||||
NSException *exception)
|
||||
: MinidumpGenerator(mach_task_self(), 0) {
|
||||
return_addresses_ = [[exception callStackReturnAddresses] retain];
|
||||
SetExceptionInformation(kExceptionType,
|
||||
kExceptionCode,
|
||||
0,
|
||||
pthread_mach_thread_np(pthread_self()));
|
||||
}
|
||||
|
||||
IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() {
|
||||
[return_addresses_ release];
|
||||
}
|
||||
|
||||
bool IosExceptionMinidumpGenerator::WriteCrashingContext(
|
||||
MDLocationDescriptor *register_location) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
return WriteCrashingContextARM(register_location);
|
||||
#elif defined(HAS_ARM64_SUPPORT)
|
||||
return WriteCrashingContextARM64(register_location);
|
||||
#else
|
||||
assert(false);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
bool IosExceptionMinidumpGenerator::WriteCrashingContextARM(
|
||||
MDLocationDescriptor *register_location) {
|
||||
TypedMDRVA<MDRawContextARM> context(&writer_);
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
*register_location = context.location();
|
||||
MDRawContextARM *context_ptr = context.get();
|
||||
memset(context_ptr, 0, sizeof(MDRawContextARM));
|
||||
context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_IOS_FP] = kExpectedFinalFp; // FP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_SP] = kExpectedFinalSp; // SP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_LR] = GetLRFromException(); // LR
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_PC] = GetPCFromException(); // PC
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_ARM64_SUPPORT
|
||||
bool IosExceptionMinidumpGenerator::WriteCrashingContextARM64(
|
||||
MDLocationDescriptor *register_location) {
|
||||
TypedMDRVA<MDRawContextARM64> context(&writer_);
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
*register_location = context.location();
|
||||
MDRawContextARM64 *context_ptr = context.get();
|
||||
memset(context_ptr, 0, sizeof(*context_ptr));
|
||||
context_ptr->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_FP] = kExpectedFinalFp; // FP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_SP] = kExpectedFinalSp; // SP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_LR] = GetLRFromException(); // LR
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_PC] = GetPCFromException(); // PC
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() {
|
||||
return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue];
|
||||
}
|
||||
|
||||
uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() {
|
||||
return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue];
|
||||
}
|
||||
|
||||
bool IosExceptionMinidumpGenerator::WriteExceptionStream(
|
||||
MDRawDirectory *exception_stream) {
|
||||
#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
|
||||
TypedMDRVA<MDRawExceptionStream> exception(&writer_);
|
||||
|
||||
if (!exception.Allocate())
|
||||
return false;
|
||||
|
||||
exception_stream->stream_type = MD_EXCEPTION_STREAM;
|
||||
exception_stream->location = exception.location();
|
||||
MDRawExceptionStream *exception_ptr = exception.get();
|
||||
exception_ptr->thread_id = pthread_mach_thread_np(pthread_self());
|
||||
|
||||
// This naming is confusing, but it is the proper translation from
|
||||
// mach naming to minidump naming.
|
||||
exception_ptr->exception_record.exception_code = kExceptionType;
|
||||
exception_ptr->exception_record.exception_flags = kExceptionCode;
|
||||
|
||||
if (!WriteCrashingContext(&exception_ptr->thread_context))
|
||||
return false;
|
||||
|
||||
exception_ptr->exception_record.exception_address = GetPCFromException();
|
||||
return true;
|
||||
#else
|
||||
return MinidumpGenerator::WriteExceptionStream(exception_stream);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
|
||||
MDRawThread *thread) {
|
||||
#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
|
||||
if (pthread_mach_thread_np(pthread_self()) != thread_id)
|
||||
return MinidumpGenerator::WriteThreadStream(thread_id, thread);
|
||||
|
||||
size_t frame_count = [return_addresses_ count];
|
||||
if (frame_count == 0)
|
||||
return false;
|
||||
UntypedMDRVA memory(&writer_);
|
||||
size_t pointer_size = sizeof(uintptr_t);
|
||||
size_t frame_record_size = 2 * pointer_size;
|
||||
size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size;
|
||||
if (!memory.Allocate(stack_size))
|
||||
return false;
|
||||
scoped_array<uint8_t> stack_memory(new uint8_t[stack_size]);
|
||||
uintptr_t sp = stack_size - pointer_size;
|
||||
uintptr_t fp = 0;
|
||||
uintptr_t lr = 0;
|
||||
for (size_t current_frame = frame_count - 1;
|
||||
current_frame > 0;
|
||||
--current_frame) {
|
||||
AppendToMemory(stack_memory.get(), sp, lr);
|
||||
sp -= pointer_size;
|
||||
AppendToMemory(stack_memory.get(), sp, fp);
|
||||
fp = sp;
|
||||
sp -= pointer_size;
|
||||
lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue];
|
||||
}
|
||||
if (!memory.Copy(stack_memory.get(), stack_size))
|
||||
return false;
|
||||
assert(sp == kExpectedFinalSp);
|
||||
assert(fp == kExpectedFinalFp);
|
||||
assert(lr == GetLRFromException());
|
||||
thread->stack.start_of_memory_range = sp;
|
||||
thread->stack.memory = memory.location();
|
||||
memory_blocks_.push_back(thread->stack);
|
||||
|
||||
if (!WriteCrashingContext(&thread->thread_context))
|
||||
return false;
|
||||
|
||||
thread->thread_id = thread_id;
|
||||
return true;
|
||||
#else
|
||||
return MinidumpGenerator::WriteThreadStream(thread_id, thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
53
src/client/linux/crash_generation/client_info.h
Normal file
53
src/client/linux/crash_generation/client_info.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
|
||||
#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CrashGenerationServer;
|
||||
|
||||
class ClientInfo {
|
||||
public:
|
||||
ClientInfo(pid_t pid, CrashGenerationServer* crash_server)
|
||||
: crash_server_(crash_server),
|
||||
pid_(pid) {}
|
||||
|
||||
CrashGenerationServer* crash_server() const { return crash_server_; }
|
||||
pid_t pid() const { return pid_; }
|
||||
|
||||
private:
|
||||
CrashGenerationServer* crash_server_;
|
||||
pid_t pid_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
|
105
src/client/linux/crash_generation/crash_generation_client.cc
Normal file
105
src/client/linux/crash_generation/crash_generation_client.cc
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
|
||||
class CrashGenerationClientImpl : public CrashGenerationClient {
|
||||
public:
|
||||
explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {}
|
||||
virtual ~CrashGenerationClientImpl() {}
|
||||
|
||||
virtual bool RequestDump(const void* blob, size_t blob_size) {
|
||||
int fds[2];
|
||||
if (sys_pipe(fds) < 0)
|
||||
return false;
|
||||
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
|
||||
|
||||
struct kernel_iovec iov;
|
||||
iov.iov_base = const_cast<void*>(blob);
|
||||
iov.iov_len = blob_size;
|
||||
|
||||
struct kernel_msghdr msg = { 0 };
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
char cmsg[kControlMsgSize] = "";
|
||||
msg.msg_control = cmsg;
|
||||
msg.msg_controllen = sizeof(cmsg);
|
||||
|
||||
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
|
||||
hdr->cmsg_level = SOL_SOCKET;
|
||||
hdr->cmsg_type = SCM_RIGHTS;
|
||||
hdr->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
|
||||
*p = fds[1];
|
||||
|
||||
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
|
||||
sys_close(fds[1]);
|
||||
if (ret < 0) {
|
||||
sys_close(fds[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for an ACK from the server.
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
|
||||
sys_close(fds[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
int server_fd_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) {
|
||||
if (server_fd < 0)
|
||||
return NULL;
|
||||
return new CrashGenerationClientImpl(server_fd);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
65
src/client/linux/crash_generation/crash_generation_client.h
Normal file
65
src/client/linux/crash_generation/crash_generation_client.h
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
|
||||
#include "common/basictypes.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// CrashGenerationClient is an interface for implementing out-of-process crash
|
||||
// dumping. The default implementation, accessed via the TryCreate() factory,
|
||||
// works in conjunction with the CrashGenerationServer to generate a minidump
|
||||
// via a remote process.
|
||||
class CrashGenerationClient {
|
||||
public:
|
||||
CrashGenerationClient() {}
|
||||
virtual ~CrashGenerationClient() {}
|
||||
|
||||
// Request the crash server to generate a dump. |blob| is an opaque
|
||||
// CrashContext pointer from exception_handler.h.
|
||||
// Returns true if the dump was successful; false otherwise.
|
||||
virtual bool RequestDump(const void* blob, size_t blob_size) = 0;
|
||||
|
||||
// Returns a new CrashGenerationClient if |server_fd| is valid and
|
||||
// connects to a CrashGenerationServer. Otherwise, return NULL.
|
||||
// The returned CrashGenerationClient* is owned by the caller of
|
||||
// this function.
|
||||
static CrashGenerationClient* TryCreate(int server_fd);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
333
src/client/linux/crash_generation/crash_generation_server.cc
Normal file
333
src/client/linux/crash_generation/crash_generation_server.cc
Normal file
@ -0,0 +1,333 @@
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "client/linux/crash_generation/crash_generation_server.h"
|
||||
#include "client/linux/crash_generation/client_info.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "common/linux/safe_readlink.h"
|
||||
|
||||
static const char kCommandQuit = 'x';
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
CrashGenerationServer::CrashGenerationServer(
|
||||
const int listen_fd,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void* dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
void* exit_context,
|
||||
bool generate_dumps,
|
||||
const string* dump_path) :
|
||||
server_fd_(listen_fd),
|
||||
dump_callback_(dump_callback),
|
||||
dump_context_(dump_context),
|
||||
exit_callback_(exit_callback),
|
||||
exit_context_(exit_context),
|
||||
generate_dumps_(generate_dumps),
|
||||
started_(false)
|
||||
{
|
||||
if (dump_path)
|
||||
dump_dir_ = *dump_path;
|
||||
else
|
||||
dump_dir_ = "/tmp";
|
||||
}
|
||||
|
||||
CrashGenerationServer::~CrashGenerationServer()
|
||||
{
|
||||
if (started_)
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool
|
||||
CrashGenerationServer::Start()
|
||||
{
|
||||
if (started_ || 0 > server_fd_)
|
||||
return false;
|
||||
|
||||
int control_pipe[2];
|
||||
if (pipe(control_pipe))
|
||||
return false;
|
||||
|
||||
if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
|
||||
return false;
|
||||
if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
|
||||
return false;
|
||||
|
||||
if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
|
||||
return false;
|
||||
|
||||
control_pipe_in_ = control_pipe[0];
|
||||
control_pipe_out_ = control_pipe[1];
|
||||
|
||||
if (pthread_create(&thread_, NULL,
|
||||
ThreadMain, reinterpret_cast<void*>(this)))
|
||||
return false;
|
||||
|
||||
started_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CrashGenerationServer::Stop()
|
||||
{
|
||||
assert(pthread_self() != thread_);
|
||||
|
||||
if (!started_)
|
||||
return;
|
||||
|
||||
HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
|
||||
|
||||
void* dummy;
|
||||
pthread_join(thread_, &dummy);
|
||||
|
||||
close(control_pipe_in_);
|
||||
close(control_pipe_out_);
|
||||
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
//static
|
||||
bool
|
||||
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
|
||||
return false;
|
||||
|
||||
static const int on = 1;
|
||||
// Enable passcred on the server end of the socket
|
||||
if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
|
||||
return false;
|
||||
|
||||
if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
|
||||
return false;
|
||||
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
|
||||
return false;
|
||||
|
||||
*client_fd = fds[0];
|
||||
*server_fd = fds[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
// The following methods/functions execute on the server thread
|
||||
|
||||
void
|
||||
CrashGenerationServer::Run()
|
||||
{
|
||||
struct pollfd pollfds[2];
|
||||
memset(&pollfds, 0, sizeof(pollfds));
|
||||
|
||||
pollfds[0].fd = server_fd_;
|
||||
pollfds[0].events = POLLIN;
|
||||
|
||||
pollfds[1].fd = control_pipe_in_;
|
||||
pollfds[1].events = POLLIN;
|
||||
|
||||
while (true) {
|
||||
// infinite timeout
|
||||
int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
|
||||
if (-1 == nevents) {
|
||||
if (EINTR == errno) {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
|
||||
return;
|
||||
|
||||
if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CrashGenerationServer::ClientEvent(short revents)
|
||||
{
|
||||
if (POLLHUP & revents)
|
||||
return false;
|
||||
assert(POLLIN & revents);
|
||||
|
||||
// A process has crashed and has signaled us by writing a datagram
|
||||
// to the death signal socket. The datagram contains the crash context needed
|
||||
// for writing the minidump as well as a file descriptor and a credentials
|
||||
// block so that they can't lie about their pid.
|
||||
|
||||
// The length of the control message:
|
||||
static const unsigned kControlMsgSize =
|
||||
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
|
||||
// The length of the regular payload:
|
||||
static const unsigned kCrashContextSize =
|
||||
sizeof(google_breakpad::ExceptionHandler::CrashContext);
|
||||
|
||||
struct msghdr msg = {0};
|
||||
struct iovec iov[1];
|
||||
char crash_context[kCrashContextSize];
|
||||
char control[kControlMsgSize];
|
||||
const ssize_t expected_msg_size = sizeof(crash_context);
|
||||
|
||||
iov[0].iov_base = crash_context;
|
||||
iov[0].iov_len = sizeof(crash_context);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
|
||||
msg.msg_control = control;
|
||||
msg.msg_controllen = kControlMsgSize;
|
||||
|
||||
const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
|
||||
if (msg_size != expected_msg_size)
|
||||
return true;
|
||||
|
||||
if (msg.msg_controllen != kControlMsgSize ||
|
||||
msg.msg_flags & ~MSG_TRUNC)
|
||||
return true;
|
||||
|
||||
// Walk the control payload and extract the file descriptor and validated pid.
|
||||
pid_t crashing_pid = -1;
|
||||
int signal_fd = -1;
|
||||
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
|
||||
hdr = CMSG_NXTHDR(&msg, hdr)) {
|
||||
if (hdr->cmsg_level != SOL_SOCKET)
|
||||
continue;
|
||||
if (hdr->cmsg_type == SCM_RIGHTS) {
|
||||
const unsigned len = hdr->cmsg_len -
|
||||
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
|
||||
assert(len % sizeof(int) == 0u);
|
||||
const unsigned num_fds = len / sizeof(int);
|
||||
if (num_fds > 1 || num_fds == 0) {
|
||||
// A nasty process could try and send us too many descriptors and
|
||||
// force a leak.
|
||||
for (unsigned i = 0; i < num_fds; ++i)
|
||||
close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
|
||||
return true;
|
||||
} else {
|
||||
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
|
||||
}
|
||||
} else if (hdr->cmsg_type == SCM_CREDENTIALS) {
|
||||
const struct ucred *cred =
|
||||
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
|
||||
crashing_pid = cred->pid;
|
||||
}
|
||||
}
|
||||
|
||||
if (crashing_pid == -1 || signal_fd == -1) {
|
||||
if (signal_fd)
|
||||
close(signal_fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
string minidump_filename;
|
||||
if (!MakeMinidumpFilename(minidump_filename))
|
||||
return true;
|
||||
|
||||
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
|
||||
crashing_pid, crash_context,
|
||||
kCrashContextSize)) {
|
||||
close(signal_fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dump_callback_) {
|
||||
ClientInfo info(crashing_pid, this);
|
||||
|
||||
dump_callback_(dump_context_, &info, &minidump_filename);
|
||||
}
|
||||
|
||||
// Send the done signal to the process: it can exit now.
|
||||
// (Closing this will make the child's sys_read unblock and return 0.)
|
||||
close(signal_fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CrashGenerationServer::ControlEvent(short revents)
|
||||
{
|
||||
if (POLLHUP & revents)
|
||||
return false;
|
||||
assert(POLLIN & revents);
|
||||
|
||||
char command;
|
||||
if (read(control_pipe_in_, &command, 1))
|
||||
return false;
|
||||
|
||||
switch (command) {
|
||||
case kCommandQuit:
|
||||
return false;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
|
||||
{
|
||||
GUID guid;
|
||||
char guidString[kGUIDStringLength+1];
|
||||
|
||||
if (!(CreateGUID(&guid)
|
||||
&& GUIDToString(&guid, guidString, sizeof(guidString))))
|
||||
return false;
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
|
||||
|
||||
outFilename = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void*
|
||||
CrashGenerationServer::ThreadMain(void *arg)
|
||||
{
|
||||
reinterpret_cast<CrashGenerationServer*>(arg)->Run();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
135
src/client/linux/crash_generation/crash_generation_server.h
Normal file
135
src/client/linux/crash_generation/crash_generation_server.h
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
||||
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ClientInfo;
|
||||
|
||||
class CrashGenerationServer {
|
||||
public:
|
||||
// WARNING: callbacks may be invoked on a different thread
|
||||
// than that which creates the CrashGenerationServer. They must
|
||||
// be thread safe.
|
||||
typedef void (*OnClientDumpRequestCallback)(void* context,
|
||||
const ClientInfo* client_info,
|
||||
const string* file_path);
|
||||
|
||||
typedef void (*OnClientExitingCallback)(void* context,
|
||||
const ClientInfo* client_info);
|
||||
|
||||
// Create an instance with the given parameters.
|
||||
//
|
||||
// Parameter listen_fd: The server fd created by CreateReportChannel().
|
||||
// Parameter dump_callback: Callback for a client crash dump request.
|
||||
// Parameter dump_context: Context for client crash dump request callback.
|
||||
// Parameter exit_callback: Callback for client process exit.
|
||||
// Parameter exit_context: Context for client exit callback.
|
||||
// Parameter generate_dumps: Whether to automatically generate dumps.
|
||||
// Client code of this class might want to generate dumps explicitly
|
||||
// in the crash dump request callback. In that case, false can be
|
||||
// passed for this parameter.
|
||||
// Parameter dump_path: Path for generating dumps; required only if true is
|
||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
||||
CrashGenerationServer(const int listen_fd,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void* dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
void* exit_context,
|
||||
bool generate_dumps,
|
||||
const string* dump_path);
|
||||
|
||||
~CrashGenerationServer();
|
||||
|
||||
// Perform initialization steps needed to start listening to clients.
|
||||
//
|
||||
// Return true if initialization is successful; false otherwise.
|
||||
bool Start();
|
||||
|
||||
// Stop the server.
|
||||
void Stop();
|
||||
|
||||
// Create a "channel" that can be used by clients to report crashes
|
||||
// to a CrashGenerationServer. |*server_fd| should be passed to
|
||||
// this class's constructor, and |*client_fd| should be passed to
|
||||
// the ExceptionHandler constructor in the client process.
|
||||
static bool CreateReportChannel(int* server_fd, int* client_fd);
|
||||
|
||||
private:
|
||||
// Run the server's event loop
|
||||
void Run();
|
||||
|
||||
// Invoked when an child process (client) event occurs
|
||||
// Returning true => "keep running", false => "exit loop"
|
||||
bool ClientEvent(short revents);
|
||||
|
||||
// Invoked when the controlling thread (main) event occurs
|
||||
// Returning true => "keep running", false => "exit loop"
|
||||
bool ControlEvent(short revents);
|
||||
|
||||
// Return a unique filename at which a minidump can be written
|
||||
bool MakeMinidumpFilename(string& outFilename);
|
||||
|
||||
// Trampoline to |Run()|
|
||||
static void* ThreadMain(void* arg);
|
||||
|
||||
int server_fd_;
|
||||
|
||||
OnClientDumpRequestCallback dump_callback_;
|
||||
void* dump_context_;
|
||||
|
||||
OnClientExitingCallback exit_callback_;
|
||||
void* exit_context_;
|
||||
|
||||
bool generate_dumps_;
|
||||
|
||||
string dump_dir_;
|
||||
|
||||
bool started_;
|
||||
|
||||
pthread_t thread_;
|
||||
int control_pipe_in_;
|
||||
int control_pipe_out_;
|
||||
|
||||
// disable these
|
||||
CrashGenerationServer(const CrashGenerationServer&);
|
||||
CrashGenerationServer& operator=(const CrashGenerationServer&);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
61
src/client/linux/dump_writer_common/mapping_info.h
Normal file
61
src/client/linux/dump_writer_common/mapping_info.h
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <list>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// One of these is produced for each mapping in the process (i.e. line in
|
||||
// /proc/$x/maps).
|
||||
struct MappingInfo {
|
||||
uintptr_t start_addr;
|
||||
size_t size;
|
||||
size_t offset; // offset into the backed file.
|
||||
bool exec; // true if the mapping has the execute bit set.
|
||||
char name[NAME_MAX];
|
||||
};
|
||||
|
||||
struct MappingEntry {
|
||||
MappingInfo first;
|
||||
uint8_t second[sizeof(MDGUID)];
|
||||
};
|
||||
|
||||
// A list of <MappingInfo, GUID>
|
||||
typedef std::list<MappingEntry> MappingList;
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
53
src/client/linux/dump_writer_common/raw_context_cpu.h
Normal file
53
src/client/linux/dump_writer_common/raw_context_cpu.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386__)
|
||||
typedef MDRawContextX86 RawContextCPU;
|
||||
#elif defined(__x86_64)
|
||||
typedef MDRawContextAMD64 RawContextCPU;
|
||||
#elif defined(__ARM_EABI__)
|
||||
typedef MDRawContextARM RawContextCPU;
|
||||
#elif defined(__aarch64__)
|
||||
typedef MDRawContextARM64 RawContextCPU;
|
||||
#elif defined(__mips__)
|
||||
typedef MDRawContextMIPS RawContextCPU;
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
305
src/client/linux/dump_writer_common/thread_info.cc
Normal file
305
src/client/linux/dump_writer_common/thread_info.cc
Normal file
@ -0,0 +1,305 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(__i386__)
|
||||
// Write a uint16_t to memory
|
||||
// out: memory location to write to
|
||||
// v: value to write.
|
||||
void U16(void* out, uint16_t v) {
|
||||
my_memcpy(out, &v, sizeof(v));
|
||||
}
|
||||
|
||||
// Write a uint32_t to memory
|
||||
// out: memory location to write to
|
||||
// v: value to write.
|
||||
void U32(void* out, uint32_t v) {
|
||||
my_memcpy(out, &v, sizeof(v));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.eip;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_X86_ALL;
|
||||
|
||||
out->dr0 = dregs[0];
|
||||
out->dr1 = dregs[1];
|
||||
out->dr2 = dregs[2];
|
||||
out->dr3 = dregs[3];
|
||||
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
||||
// format.
|
||||
out->dr6 = dregs[6];
|
||||
out->dr7 = dregs[7];
|
||||
|
||||
out->gs = regs.xgs;
|
||||
out->fs = regs.xfs;
|
||||
out->es = regs.xes;
|
||||
out->ds = regs.xds;
|
||||
|
||||
out->edi = regs.edi;
|
||||
out->esi = regs.esi;
|
||||
out->ebx = regs.ebx;
|
||||
out->edx = regs.edx;
|
||||
out->ecx = regs.ecx;
|
||||
out->eax = regs.eax;
|
||||
|
||||
out->ebp = regs.ebp;
|
||||
out->eip = regs.eip;
|
||||
out->cs = regs.xcs;
|
||||
out->eflags = regs.eflags;
|
||||
out->esp = regs.esp;
|
||||
out->ss = regs.xss;
|
||||
|
||||
out->float_save.control_word = fpregs.cwd;
|
||||
out->float_save.status_word = fpregs.swd;
|
||||
out->float_save.tag_word = fpregs.twd;
|
||||
out->float_save.error_offset = fpregs.fip;
|
||||
out->float_save.error_selector = fpregs.fcs;
|
||||
out->float_save.data_offset = fpregs.foo;
|
||||
out->float_save.data_selector = fpregs.fos;
|
||||
|
||||
// 8 registers * 10 bytes per register.
|
||||
my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8);
|
||||
|
||||
// This matches the Intel fpsave format.
|
||||
U16(out->extended_registers + 0, fpregs.cwd);
|
||||
U16(out->extended_registers + 2, fpregs.swd);
|
||||
U16(out->extended_registers + 4, fpregs.twd);
|
||||
U16(out->extended_registers + 6, fpxregs.fop);
|
||||
U32(out->extended_registers + 8, fpxregs.fip);
|
||||
U16(out->extended_registers + 12, fpxregs.fcs);
|
||||
U32(out->extended_registers + 16, fpregs.foo);
|
||||
U16(out->extended_registers + 20, fpregs.fos);
|
||||
U32(out->extended_registers + 24, fpxregs.mxcsr);
|
||||
|
||||
my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128);
|
||||
my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128);
|
||||
}
|
||||
|
||||
#elif defined(__x86_64)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.rip;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_AMD64_FULL |
|
||||
MD_CONTEXT_AMD64_SEGMENTS;
|
||||
|
||||
out->cs = regs.cs;
|
||||
|
||||
out->ds = regs.ds;
|
||||
out->es = regs.es;
|
||||
out->fs = regs.fs;
|
||||
out->gs = regs.gs;
|
||||
|
||||
out->ss = regs.ss;
|
||||
out->eflags = regs.eflags;
|
||||
|
||||
out->dr0 = dregs[0];
|
||||
out->dr1 = dregs[1];
|
||||
out->dr2 = dregs[2];
|
||||
out->dr3 = dregs[3];
|
||||
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
||||
// format.
|
||||
out->dr6 = dregs[6];
|
||||
out->dr7 = dregs[7];
|
||||
|
||||
out->rax = regs.rax;
|
||||
out->rcx = regs.rcx;
|
||||
out->rdx = regs.rdx;
|
||||
out->rbx = regs.rbx;
|
||||
|
||||
out->rsp = regs.rsp;
|
||||
|
||||
out->rbp = regs.rbp;
|
||||
out->rsi = regs.rsi;
|
||||
out->rdi = regs.rdi;
|
||||
out->r8 = regs.r8;
|
||||
out->r9 = regs.r9;
|
||||
out->r10 = regs.r10;
|
||||
out->r11 = regs.r11;
|
||||
out->r12 = regs.r12;
|
||||
out->r13 = regs.r13;
|
||||
out->r14 = regs.r14;
|
||||
out->r15 = regs.r15;
|
||||
|
||||
out->rip = regs.rip;
|
||||
|
||||
out->flt_save.control_word = fpregs.cwd;
|
||||
out->flt_save.status_word = fpregs.swd;
|
||||
out->flt_save.tag_word = fpregs.ftw;
|
||||
out->flt_save.error_opcode = fpregs.fop;
|
||||
out->flt_save.error_offset = fpregs.rip;
|
||||
out->flt_save.error_selector = 0; // We don't have this.
|
||||
out->flt_save.data_offset = fpregs.rdp;
|
||||
out->flt_save.data_selector = 0; // We don't have this.
|
||||
out->flt_save.mx_csr = fpregs.mxcsr;
|
||||
out->flt_save.mx_csr_mask = fpregs.mxcr_mask;
|
||||
|
||||
my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16);
|
||||
my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16);
|
||||
}
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.uregs[15];
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
|
||||
out->iregs[i] = regs.uregs[i];
|
||||
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
|
||||
out->cpsr = 0;
|
||||
#if !defined(__ANDROID__)
|
||||
out->float_save.fpscr = fpregs.fpsr |
|
||||
(static_cast<uint64_t>(fpregs.fpcr) << 32);
|
||||
// TODO: sort this out, actually collect floating point registers
|
||||
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.pc;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||
|
||||
out->cpsr = static_cast<uint32_t>(regs.pstate);
|
||||
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
|
||||
out->iregs[i] = regs.regs[i];
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_SP] = regs.sp;
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_PC] = regs.pc;
|
||||
|
||||
out->float_save.fpsr = fpregs.fpsr;
|
||||
out->float_save.fpcr = fpregs.fpcr;
|
||||
my_memcpy(&out->float_save.regs, &fpregs.vregs,
|
||||
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
|
||||
}
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return mcontext.pc;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
#if _MIPS_SIM == _ABI64
|
||||
out->context_flags = MD_CONTEXT_MIPS64_FULL;
|
||||
#elif _MIPS_SIM == _ABIO32
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
#else
|
||||
# error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||
out->iregs[i] = mcontext.gregs[i];
|
||||
|
||||
out->mdhi = mcontext.mdhi;
|
||||
out->mdlo = mcontext.mdlo;
|
||||
out->dsp_control = mcontext.dsp;
|
||||
|
||||
out->hi[0] = mcontext.hi1;
|
||||
out->lo[0] = mcontext.lo1;
|
||||
out->hi[1] = mcontext.hi2;
|
||||
out->lo[1] = mcontext.lo2;
|
||||
out->hi[2] = mcontext.hi3;
|
||||
out->lo[2] = mcontext.lo3;
|
||||
|
||||
out->epc = mcontext.pc;
|
||||
out->badvaddr = 0; // Not stored in mcontext
|
||||
out->status = 0; // Not stored in mcontext
|
||||
out->cause = 0; // Not stored in mcontext
|
||||
|
||||
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
||||
out->float_save.regs[i] = mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs;
|
||||
|
||||
out->float_save.fpcsr = mcontext.fpc_csr;
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
out->float_save.fir = mcontext.fpc_eir;
|
||||
#endif
|
||||
}
|
||||
#endif // __mips__
|
||||
|
||||
void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) {
|
||||
assert(gp_regs || size);
|
||||
#if defined(__mips__)
|
||||
if (gp_regs)
|
||||
*gp_regs = mcontext.gregs;
|
||||
if (size)
|
||||
*size = sizeof(mcontext.gregs);
|
||||
#else
|
||||
if (gp_regs)
|
||||
*gp_regs = ®s;
|
||||
if (size)
|
||||
*size = sizeof(regs);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) {
|
||||
assert(fp_regs || size);
|
||||
#if defined(__mips__)
|
||||
if (fp_regs)
|
||||
*fp_regs = &mcontext.fpregs;
|
||||
if (size)
|
||||
*size = sizeof(mcontext.fpregs);
|
||||
#else
|
||||
if (fp_regs)
|
||||
*fp_regs = &fpregs;
|
||||
if (size)
|
||||
*size = sizeof(fpregs);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
91
src/client/linux/dump_writer_common/thread_info.h
Normal file
91
src/client/linux/dump_writer_common/thread_info.h
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
||||
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
||||
#include "common/memory.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
#endif
|
||||
|
||||
// We produce one of these structures for each thread in the crashed process.
|
||||
struct ThreadInfo {
|
||||
pid_t tgid; // thread group id
|
||||
pid_t ppid; // parent process
|
||||
|
||||
uintptr_t stack_pointer; // thread stack pointer
|
||||
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
user_regs_struct regs;
|
||||
user_fpregs_struct fpregs;
|
||||
static const unsigned kNumDebugRegisters = 8;
|
||||
debugreg_t dregs[8];
|
||||
#if defined(__i386)
|
||||
user_fpxregs_struct fpxregs;
|
||||
#endif // defined(__i386)
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
// Mimicking how strace does this(see syscall.c, search for GETREGS)
|
||||
struct user_regs regs;
|
||||
struct user_fpregs fpregs;
|
||||
#elif defined(__aarch64__)
|
||||
// Use the structures defined in <sys/user.h>
|
||||
struct user_regs_struct regs;
|
||||
struct user_fpsimd_struct fpregs;
|
||||
#elif defined(__mips__)
|
||||
// Use the structure defined in <sys/ucontext.h>.
|
||||
mcontext_t mcontext;
|
||||
#endif
|
||||
|
||||
// Returns the instruction pointer (platform-dependent impl.).
|
||||
uintptr_t GetInstructionPointer() const;
|
||||
|
||||
// Fills a RawContextCPU using the context in the ThreadInfo object.
|
||||
void FillCPUContext(RawContextCPU* out) const;
|
||||
|
||||
// Returns the pointer and size of general purpose register area.
|
||||
void GetGeneralPurposeRegisters(void** gp_regs, size_t* size);
|
||||
|
||||
// Returns the pointer and size of float point register area.
|
||||
void GetFloatingPointRegisters(void** fp_regs, size_t* size);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
259
src/client/linux/dump_writer_common/ucontext_reader.cc
Normal file
259
src/client/linux/dump_writer_common/ucontext_reader.cc
Normal file
@ -0,0 +1,259 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Minidump defines register structures which are different from the raw
|
||||
// structures which we get from the kernel. These are platform specific
|
||||
// functions to juggle the ucontext and user structures into minidump format.
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_ESP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_EIP];
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fp) {
|
||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||
|
||||
out->context_flags = MD_CONTEXT_X86_FULL |
|
||||
MD_CONTEXT_X86_FLOATING_POINT;
|
||||
|
||||
out->gs = regs[REG_GS];
|
||||
out->fs = regs[REG_FS];
|
||||
out->es = regs[REG_ES];
|
||||
out->ds = regs[REG_DS];
|
||||
|
||||
out->edi = regs[REG_EDI];
|
||||
out->esi = regs[REG_ESI];
|
||||
out->ebx = regs[REG_EBX];
|
||||
out->edx = regs[REG_EDX];
|
||||
out->ecx = regs[REG_ECX];
|
||||
out->eax = regs[REG_EAX];
|
||||
|
||||
out->ebp = regs[REG_EBP];
|
||||
out->eip = regs[REG_EIP];
|
||||
out->cs = regs[REG_CS];
|
||||
out->eflags = regs[REG_EFL];
|
||||
out->esp = regs[REG_UESP];
|
||||
out->ss = regs[REG_SS];
|
||||
|
||||
out->float_save.control_word = fp->cw;
|
||||
out->float_save.status_word = fp->sw;
|
||||
out->float_save.tag_word = fp->tag;
|
||||
out->float_save.error_offset = fp->ipoff;
|
||||
out->float_save.error_selector = fp->cssel;
|
||||
out->float_save.data_offset = fp->dataoff;
|
||||
out->float_save.data_selector = fp->datasel;
|
||||
|
||||
// 8 registers * 10 bytes per register.
|
||||
my_memcpy(out->float_save.register_area, fp->_st, 10 * 8);
|
||||
}
|
||||
|
||||
#elif defined(__x86_64)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_RSP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_RIP];
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fpregs) {
|
||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||
|
||||
out->context_flags = MD_CONTEXT_AMD64_FULL;
|
||||
|
||||
out->cs = regs[REG_CSGSFS] & 0xffff;
|
||||
|
||||
out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
|
||||
out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
|
||||
|
||||
out->eflags = regs[REG_EFL];
|
||||
|
||||
out->rax = regs[REG_RAX];
|
||||
out->rcx = regs[REG_RCX];
|
||||
out->rdx = regs[REG_RDX];
|
||||
out->rbx = regs[REG_RBX];
|
||||
|
||||
out->rsp = regs[REG_RSP];
|
||||
out->rbp = regs[REG_RBP];
|
||||
out->rsi = regs[REG_RSI];
|
||||
out->rdi = regs[REG_RDI];
|
||||
out->r8 = regs[REG_R8];
|
||||
out->r9 = regs[REG_R9];
|
||||
out->r10 = regs[REG_R10];
|
||||
out->r11 = regs[REG_R11];
|
||||
out->r12 = regs[REG_R12];
|
||||
out->r13 = regs[REG_R13];
|
||||
out->r14 = regs[REG_R14];
|
||||
out->r15 = regs[REG_R15];
|
||||
|
||||
out->rip = regs[REG_RIP];
|
||||
|
||||
out->flt_save.control_word = fpregs->cwd;
|
||||
out->flt_save.status_word = fpregs->swd;
|
||||
out->flt_save.tag_word = fpregs->ftw;
|
||||
out->flt_save.error_opcode = fpregs->fop;
|
||||
out->flt_save.error_offset = fpregs->rip;
|
||||
out->flt_save.data_offset = fpregs->rdp;
|
||||
out->flt_save.error_selector = 0; // We don't have this.
|
||||
out->flt_save.data_selector = 0; // We don't have this.
|
||||
out->flt_save.mx_csr = fpregs->mxcsr;
|
||||
out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
|
||||
my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
|
||||
my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
|
||||
}
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.arm_sp;
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.arm_pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
out->iregs[0] = uc->uc_mcontext.arm_r0;
|
||||
out->iregs[1] = uc->uc_mcontext.arm_r1;
|
||||
out->iregs[2] = uc->uc_mcontext.arm_r2;
|
||||
out->iregs[3] = uc->uc_mcontext.arm_r3;
|
||||
out->iregs[4] = uc->uc_mcontext.arm_r4;
|
||||
out->iregs[5] = uc->uc_mcontext.arm_r5;
|
||||
out->iregs[6] = uc->uc_mcontext.arm_r6;
|
||||
out->iregs[7] = uc->uc_mcontext.arm_r7;
|
||||
out->iregs[8] = uc->uc_mcontext.arm_r8;
|
||||
out->iregs[9] = uc->uc_mcontext.arm_r9;
|
||||
out->iregs[10] = uc->uc_mcontext.arm_r10;
|
||||
|
||||
out->iregs[11] = uc->uc_mcontext.arm_fp;
|
||||
out->iregs[12] = uc->uc_mcontext.arm_ip;
|
||||
out->iregs[13] = uc->uc_mcontext.arm_sp;
|
||||
out->iregs[14] = uc->uc_mcontext.arm_lr;
|
||||
out->iregs[15] = uc->uc_mcontext.arm_pc;
|
||||
|
||||
out->cpsr = uc->uc_mcontext.arm_cpsr;
|
||||
|
||||
// TODO: fix this after fixing ExceptionHandler
|
||||
out->float_save.fpscr = 0;
|
||||
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.sp;
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct fpsimd_context* fpregs) {
|
||||
out->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||
|
||||
out->cpsr = static_cast<uint32_t>(uc->uc_mcontext.pstate);
|
||||
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
|
||||
out->iregs[i] = uc->uc_mcontext.regs[i];
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_SP] = uc->uc_mcontext.sp;
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_PC] = uc->uc_mcontext.pc;
|
||||
|
||||
out->float_save.fpsr = fpregs->fpsr;
|
||||
out->float_save.fpcr = fpregs->fpcr;
|
||||
my_memcpy(&out->float_save.regs, &fpregs->vregs,
|
||||
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
|
||||
}
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
#if _MIPS_SIM == _ABI64
|
||||
out->context_flags = MD_CONTEXT_MIPS64_FULL;
|
||||
#elif _MIPS_SIM == _ABIO32
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
#else
|
||||
#error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||
out->iregs[i] = uc->uc_mcontext.gregs[i];
|
||||
|
||||
out->mdhi = uc->uc_mcontext.mdhi;
|
||||
out->mdlo = uc->uc_mcontext.mdlo;
|
||||
|
||||
out->hi[0] = uc->uc_mcontext.hi1;
|
||||
out->hi[1] = uc->uc_mcontext.hi2;
|
||||
out->hi[2] = uc->uc_mcontext.hi3;
|
||||
out->lo[0] = uc->uc_mcontext.lo1;
|
||||
out->lo[1] = uc->uc_mcontext.lo2;
|
||||
out->lo[2] = uc->uc_mcontext.lo3;
|
||||
out->dsp_control = uc->uc_mcontext.dsp;
|
||||
|
||||
out->epc = uc->uc_mcontext.pc;
|
||||
out->badvaddr = 0; // Not reported in signal context.
|
||||
out->status = 0; // Not reported in signal context.
|
||||
out->cause = 0; // Not reported in signal context.
|
||||
|
||||
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
||||
out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i];
|
||||
|
||||
out->float_save.fpcsr = uc->uc_mcontext.fpc_csr;
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace google_breakpad
|
64
src/client/linux/dump_writer_common/ucontext_reader.h
Normal file
64
src/client/linux/dump_writer_common/ucontext_reader.h
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
||||
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
||||
#include "common/memory.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Wraps platform-dependent implementations of accessors to ucontext structs.
|
||||
struct UContextReader {
|
||||
static uintptr_t GetStackPointer(const struct ucontext* uc);
|
||||
|
||||
static uintptr_t GetInstructionPointer(const struct ucontext* uc);
|
||||
|
||||
// Juggle a arch-specific ucontext into a minidump format
|
||||
// out: the minidump structure
|
||||
// info: the collection of register structures.
|
||||
#if defined(__i386__) || defined(__x86_64)
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fp);
|
||||
#elif defined(__aarch64__)
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct fpsimd_context* fpregs);
|
||||
#else
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
@ -1,55 +0,0 @@
|
||||
CXX=g++
|
||||
CC=gcc
|
||||
|
||||
CXXFLAGS=-gstabs+ -I../../.. -Wall -D_REENTRANT
|
||||
LDFLAGS=-lpthread
|
||||
|
||||
OBJ_DIR=.
|
||||
BIN_DIR=.
|
||||
|
||||
THREAD_SRC=linux_thread.cc
|
||||
SHARE_SRC=../../minidump_file_writer.cc\
|
||||
../../../common/string_conversion.cc\
|
||||
../../../common/linux/file_id.cc\
|
||||
minidump_generator.cc
|
||||
HANDLER_SRC=exception_handler.cc\
|
||||
../../../common/linux/guid_creator.cc
|
||||
SHARE_C_SRC=../../../common/convert_UTF.c
|
||||
|
||||
THREAD_TEST_SRC=linux_thread_test.cc
|
||||
MINIDUMP_TEST_SRC=minidump_test.cc
|
||||
EXCEPTION_TEST_SRC=exception_handler_test.cc
|
||||
|
||||
THREAD_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(THREAD_SRC))
|
||||
SHARE_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(SHARE_SRC))
|
||||
HANDLER_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(HANDLER_SRC))
|
||||
SHARE_C_OBJ=$(patsubst %.c,$(OBJ_DIR)/%.o,$(SHARE_C_SRC)) md5.o
|
||||
THREAD_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(THREAD_TEST_SRC))\
|
||||
$(THREAD_OBJ)
|
||||
MINIDUMP_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(MINIDUMP_TEST_SRC))\
|
||||
$(THREAD_OBJ) $(SHARE_OBJ) $(SHARE_C_OBJ)
|
||||
EXCEPTION_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(EXCEPTION_TEST_SRC))\
|
||||
$(THREAD_OBJ) $(SHARE_OBJ) $(SHARE_C_OBJ) $(HANDLER_OBJ)
|
||||
|
||||
BIN=$(BIN_DIR)/minidump_test\
|
||||
$(BIN_DIR)/linux_thread_test\
|
||||
$(BIN_DIR)/exception_handler_test
|
||||
|
||||
.PHONY:all clean
|
||||
|
||||
all:$(BIN)
|
||||
|
||||
$(BIN_DIR)/linux_thread_test:$(THREAD_TEST_OBJ)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
$(BIN_DIR)/minidump_test:$(MINIDUMP_TEST_OBJ)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
$(BIN_DIR)/exception_handler_test:$(EXCEPTION_TEST_OBJ)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
md5.o:../../../common/md5.c
|
||||
$(CC) $(CXXFLAGS) -c $^
|
||||
|
||||
clean:
|
||||
rm -f $(BIN) *.o *.dmp
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Li Liu
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -29,277 +27,739 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
// The ExceptionHandler object installs signal handlers for a number of
|
||||
// signals. We rely on the signal handler running on the thread which crashed
|
||||
// in order to identify it. This is true of the synchronous signals (SEGV etc),
|
||||
// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
|
||||
// uses ExceptionHandler, you need to use tgkill to direct it to the current
|
||||
// thread.
|
||||
//
|
||||
// The signal flow looks like this:
|
||||
//
|
||||
// SignalHandler (uses a global stack of ExceptionHandler objects to find
|
||||
// | one to handle the signal. If the first rejects it, try
|
||||
// | the second etc...)
|
||||
// V
|
||||
// HandleSignal ----------------------------| (clones a new process which
|
||||
// | | shares an address space with
|
||||
// (wait for cloned | the crashed process. This
|
||||
// process) | allows us to ptrace the crashed
|
||||
// | | process)
|
||||
// V V
|
||||
// (set signal handler to ThreadEntry (static function to bounce
|
||||
// SIG_DFL and rethrow, | back into the object)
|
||||
// killing the crashed |
|
||||
// process) V
|
||||
// DoDump (writes minidump)
|
||||
// |
|
||||
// V
|
||||
// sys_exit
|
||||
//
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <linux/limits.h>
|
||||
// This code is a little fragmented. Different functions of the ExceptionHandler
|
||||
// class run in a number of different contexts. Some of them run in a normal
|
||||
// context and are easy to code, others run in a compromised context and the
|
||||
// restrictions at the top of minidump_writer.cc apply: no libc and use the
|
||||
// alternative malloc. Each function should have comment above it detailing the
|
||||
// context which it runs in.
|
||||
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/signal.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/user.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/basictypes.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/memory.h"
|
||||
#include "client/linux/log/log.h"
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "linux/sched.h"
|
||||
#endif
|
||||
|
||||
#ifndef PR_SET_PTRACER
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
|
||||
// A wrapper for the tgkill syscall: send a signal to a specific thread.
|
||||
static int tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Signals that we are interested.
|
||||
int SigTable[] = {
|
||||
#if defined(SIGSEGV)
|
||||
SIGSEGV,
|
||||
#endif
|
||||
#ifdef SIGABRT
|
||||
SIGABRT,
|
||||
#endif
|
||||
#ifdef SIGFPE
|
||||
SIGFPE,
|
||||
#endif
|
||||
#ifdef SIGILL
|
||||
SIGILL,
|
||||
#endif
|
||||
#ifdef SIGBUS
|
||||
SIGBUS,
|
||||
#endif
|
||||
namespace {
|
||||
// The list of signals which we consider to be crashes. The default action for
|
||||
// all these signals must be Core (see man 7 signal) because we rethrow the
|
||||
// signal after handling it and expect that it'll be fatal.
|
||||
const int kExceptionSignals[] = {
|
||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP
|
||||
};
|
||||
const int kNumHandledSignals =
|
||||
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
|
||||
struct sigaction old_handlers[kNumHandledSignals];
|
||||
bool handlers_installed = false;
|
||||
|
||||
std::vector<ExceptionHandler*> *ExceptionHandler::handler_stack_ = NULL;
|
||||
int ExceptionHandler::handler_stack_index_ = 0;
|
||||
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
|
||||
PTHREAD_MUTEX_INITIALIZER;
|
||||
// InstallAlternateStackLocked will store the newly installed stack in new_stack
|
||||
// and (if it exists) the previously installed stack in old_stack.
|
||||
stack_t old_stack;
|
||||
stack_t new_stack;
|
||||
bool stack_installed = false;
|
||||
|
||||
ExceptionHandler::ExceptionHandler(const string &dump_path,
|
||||
// Create an alternative stack to run the signal handlers on. This is done since
|
||||
// the signal might have been caused by a stack overflow.
|
||||
// Runs before crashing: normal context.
|
||||
void InstallAlternateStackLocked() {
|
||||
if (stack_installed)
|
||||
return;
|
||||
|
||||
memset(&old_stack, 0, sizeof(old_stack));
|
||||
memset(&new_stack, 0, sizeof(new_stack));
|
||||
|
||||
// SIGSTKSZ may be too small to prevent the signal handlers from overrunning
|
||||
// the alternative stack. Ensure that the size of the alternative stack is
|
||||
// large enough.
|
||||
static const unsigned kSigStackSize = std::max(16384, SIGSTKSZ);
|
||||
|
||||
// Only set an alternative stack if there isn't already one, or if the current
|
||||
// one is too small.
|
||||
if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
|
||||
old_stack.ss_size < kSigStackSize) {
|
||||
new_stack.ss_sp = calloc(1, kSigStackSize);
|
||||
new_stack.ss_size = kSigStackSize;
|
||||
|
||||
if (sys_sigaltstack(&new_stack, NULL) == -1) {
|
||||
free(new_stack.ss_sp);
|
||||
return;
|
||||
}
|
||||
stack_installed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
void RestoreAlternateStackLocked() {
|
||||
if (!stack_installed)
|
||||
return;
|
||||
|
||||
stack_t current_stack;
|
||||
if (sys_sigaltstack(NULL, ¤t_stack) == -1)
|
||||
return;
|
||||
|
||||
// Only restore the old_stack if the current alternative stack is the one
|
||||
// installed by the call to InstallAlternateStackLocked.
|
||||
if (current_stack.ss_sp == new_stack.ss_sp) {
|
||||
if (old_stack.ss_sp) {
|
||||
if (sys_sigaltstack(&old_stack, NULL) == -1)
|
||||
return;
|
||||
} else {
|
||||
stack_t disable_stack;
|
||||
disable_stack.ss_flags = SS_DISABLE;
|
||||
if (sys_sigaltstack(&disable_stack, NULL) == -1)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
free(new_stack.ss_sp);
|
||||
stack_installed = false;
|
||||
}
|
||||
|
||||
void InstallDefaultHandler(int sig) {
|
||||
#if defined(__ANDROID__)
|
||||
// Android L+ expose signal and sigaction symbols that override the system
|
||||
// ones. There is a bug in these functions where a request to set the handler
|
||||
// to SIG_DFL is ignored. In that case, an infinite loop is entered as the
|
||||
// signal is repeatedly sent to breakpad's signal handler.
|
||||
// To work around this, directly call the system's sigaction.
|
||||
struct kernel_sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sys_sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler_ = SIG_DFL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t));
|
||||
#else
|
||||
signal(sig, SIG_DFL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The global exception handler stack. This is needed because there may exist
|
||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||
// registered in this stack.
|
||||
std::vector<ExceptionHandler*>* g_handler_stack_ = NULL;
|
||||
pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// sizeof(CrashContext) can be too big w.r.t the size of alternatate stack
|
||||
// for SignalHandler(). Keep the crash context as a .bss field. Exception
|
||||
// handlers are serialized by the |g_handler_stack_mutex_| and at most one at a
|
||||
// time can use |g_crash_context_|.
|
||||
ExceptionHandler::CrashContext g_crash_context_;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler)
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const int server_fd)
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
dump_path_(),
|
||||
installed_handler_(install_handler) {
|
||||
set_dump_path(dump_path);
|
||||
minidump_descriptor_(descriptor),
|
||||
crash_handler_(NULL) {
|
||||
if (server_fd >= 0)
|
||||
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
||||
act_.sa_handler = HandleException;
|
||||
act_.sa_flags = SA_ONSTACK;
|
||||
sigemptyset(&act_.sa_mask);
|
||||
// now, make sure we're blocking all the signals we are handling
|
||||
// when we're handling any of them
|
||||
for ( size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) {
|
||||
sigaddset(&act_.sa_mask, SigTable[i]);
|
||||
}
|
||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
|
||||
!minidump_descriptor_.IsMicrodumpOnConsole())
|
||||
minidump_descriptor_.UpdatePath();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole())
|
||||
logger::initializeCrashLogWriter();
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
|
||||
// Pre-fault the crash context struct. This is to avoid failing due to OOM
|
||||
// if handling an exception when the process ran out of virtual memory.
|
||||
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
|
||||
|
||||
if (!g_handler_stack_)
|
||||
g_handler_stack_ = new std::vector<ExceptionHandler*>;
|
||||
if (install_handler) {
|
||||
SetupHandler();
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
if (handler_stack_ == NULL)
|
||||
handler_stack_ = new std::vector<ExceptionHandler *>;
|
||||
handler_stack_->push_back(this);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
InstallAlternateStackLocked();
|
||||
InstallHandlersLocked();
|
||||
}
|
||||
g_handler_stack_->push_back(this);
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::~ExceptionHandler() {
|
||||
TeardownAllHandler();
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
if (handler_stack_->back() == this) {
|
||||
handler_stack_->pop_back();
|
||||
} else {
|
||||
fprintf(stderr, "warning: removing Breakpad handler out of order\n");
|
||||
for (std::vector<ExceptionHandler *>::iterator iterator =
|
||||
handler_stack_->begin();
|
||||
iterator != handler_stack_->end();
|
||||
++iterator) {
|
||||
if (*iterator == this) {
|
||||
handler_stack_->erase(iterator);
|
||||
}
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
std::vector<ExceptionHandler*>::iterator handler =
|
||||
std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this);
|
||||
g_handler_stack_->erase(handler);
|
||||
if (g_handler_stack_->empty()) {
|
||||
delete g_handler_stack_;
|
||||
g_handler_stack_ = NULL;
|
||||
RestoreAlternateStackLocked();
|
||||
RestoreHandlersLocked();
|
||||
}
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
// static
|
||||
bool ExceptionHandler::InstallHandlersLocked() {
|
||||
if (handlers_installed)
|
||||
return false;
|
||||
|
||||
// Fail if unable to store all the old handlers.
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
// Mask all exception signals when we're handling one of them.
|
||||
for (int i = 0; i < kNumHandledSignals; ++i)
|
||||
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
|
||||
|
||||
sa.sa_sigaction = SignalHandler;
|
||||
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) {
|
||||
// At this point it is impractical to back out changes, and so failure to
|
||||
// install a signal is intentionally ignored.
|
||||
}
|
||||
}
|
||||
|
||||
if (handler_stack_->empty()) {
|
||||
// When destroying the last ExceptionHandler that installed a handler,
|
||||
// clean up the handler stack.
|
||||
delete handler_stack_;
|
||||
handler_stack_ = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
bool success = InternalWriteMinidump(0, 0, NULL);
|
||||
UpdateNextID();
|
||||
return success;
|
||||
handlers_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidump(const string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context) {
|
||||
ExceptionHandler handler(dump_path, NULL, callback,
|
||||
callback_context, false);
|
||||
return handler.InternalWriteMinidump(0, 0, NULL);
|
||||
}
|
||||
|
||||
void ExceptionHandler::SetupHandler() {
|
||||
// Signal on a different stack to avoid using the stack
|
||||
// of the crashing thread.
|
||||
struct sigaltstack sig_stack;
|
||||
sig_stack.ss_sp = malloc(MINSIGSTKSZ);
|
||||
if (sig_stack.ss_sp == NULL)
|
||||
void ExceptionHandler::RestoreHandlersLocked() {
|
||||
if (!handlers_installed)
|
||||
return;
|
||||
sig_stack.ss_size = MINSIGSTKSZ;
|
||||
sig_stack.ss_flags = 0;
|
||||
|
||||
if (sigaltstack(&sig_stack, NULL) < 0)
|
||||
return;
|
||||
for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i)
|
||||
SetupHandler(SigTable[i]);
|
||||
}
|
||||
|
||||
void ExceptionHandler::SetupHandler(int signo) {
|
||||
|
||||
// We're storing pointers to the old signal action
|
||||
// structure, rather than copying the structure
|
||||
// because we can't count on the sa_mask field to
|
||||
// be scalar.
|
||||
struct sigaction *old_act = &old_actions_[signo];
|
||||
|
||||
if (sigaction(signo, &act_, old_act) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
void ExceptionHandler::TeardownHandler(int signo) {
|
||||
TeardownHandler(signo, NULL);
|
||||
}
|
||||
|
||||
void ExceptionHandler::TeardownHandler(int signo, struct sigaction *final_handler) {
|
||||
if (old_actions_[signo].sa_handler) {
|
||||
struct sigaction *act = &old_actions_[signo];
|
||||
sigaction(signo, act, final_handler);
|
||||
memset(&old_actions_[signo], 0x0, sizeof(struct sigaction));
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionHandler::TeardownAllHandler() {
|
||||
for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) {
|
||||
TeardownHandler(SigTable[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void ExceptionHandler::HandleException(int signo) {
|
||||
// In Linux, the context information about the signal is put on the stack of
|
||||
// the signal handler frame as value parameter. For some reasons, the
|
||||
// prototype of the handler doesn't declare this information as parameter, we
|
||||
// will do it by hand. It is the second parameter above the signal number.
|
||||
// However, if we are being called by another signal handler passing the
|
||||
// signal up the chain, then we may not have this random extra parameter,
|
||||
// so we may have to walk the stack to find it. We do the actual work
|
||||
// on another thread, where it's a little safer, but we want the ebp
|
||||
// from this frame to find it.
|
||||
uintptr_t current_ebp = 0;
|
||||
asm volatile ("movl %%ebp, %0"
|
||||
:"=m"(current_ebp));
|
||||
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
ExceptionHandler *current_handler =
|
||||
handler_stack_->at(handler_stack_->size() - ++handler_stack_index_);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
|
||||
// Restore original handler.
|
||||
struct sigaction old_action;
|
||||
current_handler->TeardownHandler(signo, &old_action);
|
||||
|
||||
struct sigcontext *sig_ctx = NULL;
|
||||
if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) {
|
||||
// Fully handled this exception, safe to exit.
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
// Exception not fully handled, will call the next handler in stack to
|
||||
// process it.
|
||||
if (old_action.sa_handler != NULL && sig_ctx != NULL) {
|
||||
|
||||
// Have our own typedef, because of the comment above w.r.t signal
|
||||
// context on the stack
|
||||
typedef void (*SignalHandler)(int signo, struct sigcontext);
|
||||
|
||||
SignalHandler old_handler =
|
||||
reinterpret_cast<SignalHandler>(old_action.sa_handler);
|
||||
|
||||
sigset_t old_set;
|
||||
// Use SIG_BLOCK here because we don't want to unblock a signal
|
||||
// that the signal handler we're currently in needs to block
|
||||
sigprocmask(SIG_BLOCK, &old_action.sa_mask, &old_set);
|
||||
old_handler(signo, *sig_ctx);
|
||||
sigprocmask(SIG_SETMASK, &old_set, NULL);
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
|
||||
InstallDefaultHandler(kExceptionSignals[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
current_handler->SetupHandler(signo);
|
||||
--handler_stack_index_;
|
||||
// All the handlers in stack have been invoked to handle the exception,
|
||||
// normally the process should be terminated and should not reach here.
|
||||
// In case we got here, ask the OS to handle it to avoid endless loop,
|
||||
// normally the OS will generate a core and termiate the process. This
|
||||
// may be desired to debug the program.
|
||||
if (handler_stack_index_ == 0)
|
||||
signal(signo, SIG_DFL);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
handlers_installed = false;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::InternalWriteMinidump(int signo,
|
||||
uintptr_t sighandler_ebp,
|
||||
struct sigcontext **sig_ctx) {
|
||||
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
|
||||
// crash_handler_ = callback;
|
||||
// }
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
// static
|
||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// All the exception signals are blocked at this point.
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
|
||||
// Sometimes, Breakpad runs inside a process where some other buggy code
|
||||
// saves and restores signal handlers temporarily with 'signal'
|
||||
// instead of 'sigaction'. This loses the SA_SIGINFO flag associated
|
||||
// with this function. As a consequence, the values of 'info' and 'uc'
|
||||
// become totally bogus, generally inducing a crash.
|
||||
//
|
||||
// The following code tries to detect this case. When it does, it
|
||||
// resets the signal handlers with sigaction + SA_SIGINFO and returns.
|
||||
// This forces the signal to be thrown again, but this time the kernel
|
||||
// will call the function with the right arguments.
|
||||
struct sigaction cur_handler;
|
||||
if (sigaction(sig, NULL, &cur_handler) == 0 &&
|
||||
(cur_handler.sa_flags & SA_SIGINFO) == 0) {
|
||||
// Reset signal handler with the right flags.
|
||||
sigemptyset(&cur_handler.sa_mask);
|
||||
sigaddset(&cur_handler.sa_mask, sig);
|
||||
|
||||
cur_handler.sa_sigaction = SignalHandler;
|
||||
cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||
|
||||
if (sigaction(sig, &cur_handler, NULL) == -1) {
|
||||
// When resetting the handler fails, try to reset the
|
||||
// default one to avoid an infinite loop here.
|
||||
InstallDefaultHandler(sig);
|
||||
}
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
return;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) {
|
||||
handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc);
|
||||
}
|
||||
|
||||
// Upon returning from this signal handler, sig will become unmasked and then
|
||||
// it will be retriggered. If one of the ExceptionHandlers handled it
|
||||
// successfully, restore the default handler. Otherwise, restore the
|
||||
// previously installed handler. Then, when the signal is retriggered, it will
|
||||
// be delivered to the appropriate handler.
|
||||
if (handled) {
|
||||
InstallDefaultHandler(sig);
|
||||
} else {
|
||||
RestoreHandlersLocked();
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
|
||||
// info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise).
|
||||
if (info->si_code <= 0 || sig == SIGABRT) {
|
||||
// This signal was triggered by somebody sending us the signal with kill().
|
||||
// In order to retrigger it, we have to queue a new signal by calling
|
||||
// kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
|
||||
// due to the kernel sending a SIGABRT from a user request via SysRQ.
|
||||
if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
|
||||
// If we failed to kill ourselves (e.g. because a sandbox disallows us
|
||||
// to do so), we instead resort to terminating our process. This will
|
||||
// result in an incorrect exit code.
|
||||
_exit(1);
|
||||
}
|
||||
} else {
|
||||
// This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
|
||||
// No need to reissue the signal. It will automatically trigger again,
|
||||
// when we return from the signal handler.
|
||||
}
|
||||
}
|
||||
|
||||
struct ThreadArgument {
|
||||
pid_t pid; // the crashing process
|
||||
const MinidumpDescriptor* minidump_descriptor;
|
||||
ExceptionHandler* handler;
|
||||
const void* context; // a CrashContext structure
|
||||
size_t context_size;
|
||||
};
|
||||
|
||||
// This is the entry function for the cloned process. We are in a compromised
|
||||
// context here: see the top of the file.
|
||||
// static
|
||||
int ExceptionHandler::ThreadEntry(void *arg) {
|
||||
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
|
||||
|
||||
// Block here until the crashing process unblocks us when
|
||||
// we're allowed to use ptrace
|
||||
thread_arg->handler->WaitForContinueSignal();
|
||||
|
||||
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
|
||||
thread_arg->context_size) == false;
|
||||
}
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
if (filter_ && !filter_(callback_context_))
|
||||
return false;
|
||||
|
||||
bool success = false;
|
||||
// Block all the signals we want to process when writting minidump.
|
||||
// We don't want it to be interrupted.
|
||||
sigset_t sig_blocked, sig_old;
|
||||
bool blocked = true;
|
||||
sigfillset(&sig_blocked);
|
||||
for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i)
|
||||
sigdelset(&sig_blocked, SigTable[i]);
|
||||
if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) {
|
||||
blocked = false;
|
||||
fprintf(stderr, "google_breakpad::ExceptionHandler::HandleException: "
|
||||
"failed to block signals.\n");
|
||||
// Allow ourselves to be dumped if the signal is trusted.
|
||||
bool signal_trusted = info->si_code > 0;
|
||||
bool signal_pid_trusted = info->si_code == SI_USER ||
|
||||
info->si_code == SI_TKILL;
|
||||
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
|
||||
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
success = minidump_generator_.WriteMinidumpToFile(
|
||||
next_minidump_path_c_, signo, sighandler_ebp, sig_ctx);
|
||||
// Fill in all the holes in the struct to make Valgrind happy.
|
||||
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
|
||||
memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&g_crash_context_.context, uc, sizeof(struct ucontext));
|
||||
#if defined(__aarch64__)
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
struct fpsimd_context* fp_ptr =
|
||||
(struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
|
||||
if (fp_ptr->head.magic == FPSIMD_MAGIC) {
|
||||
memcpy(&g_crash_context_.float_state, fp_ptr,
|
||||
sizeof(g_crash_context_.float_state));
|
||||
}
|
||||
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
// FP state is not part of user ABI on ARM Linux.
|
||||
// In case of MIPS Linux FP state is already part of struct ucontext
|
||||
// and 'float_state' is not a member of CrashContext.
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
if (uc_ptr->uc_mcontext.fpregs) {
|
||||
memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(g_crash_context_.float_state));
|
||||
}
|
||||
#endif
|
||||
g_crash_context_.tid = syscall(__NR_gettid);
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_),
|
||||
callback_context_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GenerateDump(&g_crash_context_);
|
||||
}
|
||||
|
||||
// Unblock the signals.
|
||||
if (blocked) {
|
||||
sigprocmask(SIG_SETMASK, &sig_old, NULL);
|
||||
// This is a public interface to HandleSignal that allows the client to
|
||||
// generate a crash dump. This function may run in a compromised context.
|
||||
bool ExceptionHandler::SimulateSignalDelivery(int sig) {
|
||||
siginfo_t siginfo = {};
|
||||
// Mimic a trusted signal to allow tracing the process (see
|
||||
// ExceptionHandler::HandleSignal().
|
||||
siginfo.si_code = SI_USER;
|
||||
siginfo.si_pid = getpid();
|
||||
struct ucontext context;
|
||||
getcontext(&context);
|
||||
return HandleSignal(sig, &siginfo, &context);
|
||||
}
|
||||
|
||||
// This function may run in a compromised context: see the top of the file.
|
||||
bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
if (IsOutOfProcess())
|
||||
return crash_generation_client_->RequestDump(context, sizeof(*context));
|
||||
|
||||
// Allocating too much stack isn't a problem, and better to err on the side
|
||||
// of caution than smash it into random locations.
|
||||
static const unsigned kChildStackSize = 16000;
|
||||
PageAllocator allocator;
|
||||
uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize));
|
||||
if (!stack)
|
||||
return false;
|
||||
// clone() needs the top-most address. (scrub just to be safe)
|
||||
stack += kChildStackSize;
|
||||
my_memset(stack - 16, 0, 16);
|
||||
|
||||
ThreadArgument thread_arg;
|
||||
thread_arg.handler = this;
|
||||
thread_arg.minidump_descriptor = &minidump_descriptor_;
|
||||
thread_arg.pid = getpid();
|
||||
thread_arg.context = context;
|
||||
thread_arg.context_size = sizeof(*context);
|
||||
|
||||
// We need to explicitly enable ptrace of parent processes on some
|
||||
// kernels, but we need to know the PID of the cloned process before we
|
||||
// can do this. Create a pipe here which we can use to block the
|
||||
// cloned process after creating it, until we have explicitly enabled ptrace
|
||||
if (sys_pipe(fdes) == -1) {
|
||||
// Creating the pipe failed. We'll log an error but carry on anyway,
|
||||
// as we'll probably still get a useful crash report. All that will happen
|
||||
// is the write() and read() calls will fail with EBADF
|
||||
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump "
|
||||
"sys_pipe failed:";
|
||||
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
|
||||
// Ensure fdes[0] and fdes[1] are invalid file descriptors.
|
||||
fdes[0] = fdes[1] = -1;
|
||||
}
|
||||
|
||||
const pid_t child = sys_clone(
|
||||
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
&thread_arg, NULL, NULL, NULL);
|
||||
if (child == -1) {
|
||||
sys_close(fdes[0]);
|
||||
sys_close(fdes[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow the child to ptrace us
|
||||
sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
||||
SendContinueSignalToChild();
|
||||
int status;
|
||||
const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
|
||||
|
||||
sys_close(fdes[0]);
|
||||
sys_close(fdes[1]);
|
||||
|
||||
if (r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
}
|
||||
|
||||
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
if (callback_)
|
||||
success = callback_(dump_path_c_, next_minidump_id_c_,
|
||||
callback_context_, success);
|
||||
success = callback_(minidump_descriptor_, callback_context_, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
void ExceptionHandler::UpdateNextID() {
|
||||
GUID guid;
|
||||
char guid_str[kGUIDStringLength + 1];
|
||||
if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
|
||||
next_minidump_id_ = guid_str;
|
||||
next_minidump_id_c_ = next_minidump_id_.c_str();
|
||||
|
||||
char minidump_path[PATH_MAX];
|
||||
snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
|
||||
dump_path_c_,
|
||||
guid_str);
|
||||
|
||||
next_minidump_path_ = minidump_path;
|
||||
next_minidump_path_c_ = next_minidump_path_.c_str();
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
void ExceptionHandler::SendContinueSignalToChild() {
|
||||
static const char okToContinueMessage = 'a';
|
||||
int r;
|
||||
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
|
||||
if (r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild "
|
||||
"sys_write failed:";
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the cloned process.
|
||||
void ExceptionHandler::WaitForContinueSignal() {
|
||||
int r;
|
||||
char receivedMessage;
|
||||
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
|
||||
if (r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::WaitForContinueSignal "
|
||||
"sys_read failed:";
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the cloned process.
|
||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size) {
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||
return google_breakpad::WriteMicrodump(
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
*minidump_descriptor_.microdump_extra_info());
|
||||
}
|
||||
if (minidump_descriptor_.IsFD()) {
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
}
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidump(const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
MinidumpDescriptor descriptor(dump_path);
|
||||
ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
|
||||
return eh.WriteMinidump();
|
||||
}
|
||||
|
||||
// In order to making using EBP to calculate the desired value for ESP
|
||||
// a valid operation, ensure that this function is compiled with a
|
||||
// frame pointer using the following attribute. This attribute
|
||||
// is supported on GCC but not on clang.
|
||||
#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__)
|
||||
__attribute__((optimize("no-omit-frame-pointer")))
|
||||
#endif
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
|
||||
!minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||
// Update the path of the minidump so that this can be called multiple times
|
||||
// and new files are created for each minidump. This is done before the
|
||||
// generation happens, as clients may want to access the MinidumpDescriptor
|
||||
// after this call to find the exact path to the minidump file.
|
||||
minidump_descriptor_.UpdatePath();
|
||||
} else if (minidump_descriptor_.IsFD()) {
|
||||
// Reposition the FD to its beginning and resize it to get rid of the
|
||||
// previous minidump info.
|
||||
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
|
||||
ignore_result(ftruncate(minidump_descriptor_.fd(), 0));
|
||||
}
|
||||
|
||||
// Allow this process to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
|
||||
CrashContext context;
|
||||
int getcontext_result = getcontext(&context.context);
|
||||
if (getcontext_result)
|
||||
return false;
|
||||
|
||||
#if defined(__i386__)
|
||||
// In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved
|
||||
// from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer
|
||||
// and it only makes sense when running in kernel mode with a different stack
|
||||
// pointer. When WriteMiniDump is called during normal processing REG_UESP is
|
||||
// zero which leads to bad minidump files.
|
||||
if (!context.context.uc_mcontext.gregs[REG_UESP]) {
|
||||
// If REG_UESP is set to REG_ESP then that includes the stack space for the
|
||||
// CrashContext object in this function, which is about 128 KB. Since the
|
||||
// Linux dumper only records 32 KB of stack this would mean that nothing
|
||||
// useful would be recorded. A better option is to set REG_UESP to REG_EBP,
|
||||
// perhaps with a small negative offset in case there is any code that
|
||||
// objects to them being equal.
|
||||
context.context.uc_mcontext.gregs[REG_UESP] =
|
||||
context.context.uc_mcontext.gregs[REG_EBP] - 16;
|
||||
// The stack saving is based off of REG_ESP so it must be set to match the
|
||||
// new REG_UESP.
|
||||
context.context.uc_mcontext.gregs[REG_ESP] =
|
||||
context.context.uc_mcontext.gregs[REG_UESP];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__)
|
||||
// FPU state is not part of ARM EABI ucontext_t.
|
||||
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
#endif
|
||||
context.tid = sys_gettid();
|
||||
|
||||
// Add an exception stream to the minidump for better reporting.
|
||||
memset(&context.siginfo, 0, sizeof(context.siginfo));
|
||||
context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
|
||||
#if defined(__i386__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]);
|
||||
#elif defined(__x86_64__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]);
|
||||
#elif defined(__arm__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
|
||||
#elif defined(__aarch64__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
|
||||
#elif defined(__mips__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
||||
return GenerateDump(&context);
|
||||
}
|
||||
|
||||
void ExceptionHandler::AddMappingInfo(const string& name,
|
||||
const uint8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
size_t file_offset) {
|
||||
MappingInfo info;
|
||||
info.start_addr = start_address;
|
||||
info.size = mapping_size;
|
||||
info.offset = file_offset;
|
||||
strncpy(info.name, name.c_str(), sizeof(info.name) - 1);
|
||||
info.name[sizeof(info.name) - 1] = '\0';
|
||||
|
||||
MappingEntry mapping;
|
||||
mapping.first = info;
|
||||
memcpy(mapping.second, identifier, sizeof(MDGUID));
|
||||
mapping_list_.push_back(mapping);
|
||||
}
|
||||
|
||||
void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
|
||||
AppMemoryList::iterator iter =
|
||||
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
|
||||
if (iter != app_memory_list_.end()) {
|
||||
// Don't allow registering the same pointer twice.
|
||||
return;
|
||||
}
|
||||
|
||||
AppMemory app_memory;
|
||||
app_memory.ptr = ptr;
|
||||
app_memory.length = length;
|
||||
app_memory_list_.push_back(app_memory);
|
||||
}
|
||||
|
||||
void ExceptionHandler::UnregisterAppMemory(void* ptr) {
|
||||
AppMemoryList::iterator iter =
|
||||
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
|
||||
if (iter != app_memory_list_.end()) {
|
||||
app_memory_list_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
|
||||
pid_t child_blamed_thread,
|
||||
const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
// This function is not run in a compromised context.
|
||||
MinidumpDescriptor descriptor(dump_path);
|
||||
descriptor.UpdatePath();
|
||||
if (!google_breakpad::WriteMinidump(descriptor.path(),
|
||||
child,
|
||||
child_blamed_thread))
|
||||
return false;
|
||||
|
||||
return callback ? callback(descriptor, callback_context, true) : true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Li Liu
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -29,26 +27,25 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
|
||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
|
||||
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <signal.h>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#include "client/linux/handler/minidump_generator.h"
|
||||
#include <string>
|
||||
|
||||
// Context information when exception occured.
|
||||
struct sigcontex;
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
//
|
||||
// ExceptionHandler
|
||||
//
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
@ -67,18 +64,18 @@ using std::string;
|
||||
// use different minidump callbacks for different call sites.
|
||||
//
|
||||
// In either case, a callback function is called when a minidump is written,
|
||||
// which receives the unqiue id of the minidump. The caller can use this
|
||||
// id to collect and write additional application state, and to launch an
|
||||
// external crash-reporting application.
|
||||
// which receives the full path or file descriptor of the minidump. The
|
||||
// caller can collect and write additional application state to that minidump,
|
||||
// and launch an external crash-reporting application.
|
||||
//
|
||||
// Caller should try to make the callbacks as crash-friendly as possible,
|
||||
// it should avoid use heap memory allocation as much as possible.
|
||||
//
|
||||
|
||||
class ExceptionHandler {
|
||||
public:
|
||||
// A callback function to run before Breakpad performs any substantial
|
||||
// processing of an exception. A FilterCallback is called before writing
|
||||
// a minidump. context is the parameter supplied by the user as
|
||||
// a minidump. |context| is the parameter supplied by the user as
|
||||
// callback_context when the handler was created.
|
||||
//
|
||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
||||
@ -88,10 +85,10 @@ class ExceptionHandler {
|
||||
typedef bool (*FilterCallback)(void *context);
|
||||
|
||||
// A callback function to run after the minidump has been written.
|
||||
// minidump_id is a unique id for the dump, so the minidump
|
||||
// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
|
||||
// by the user as callback_context when the handler was created. succeeded
|
||||
// indicates whether a minidump file was successfully written.
|
||||
// |descriptor| contains the file descriptor or file path containing the
|
||||
// minidump. |context| is the parameter supplied by the user as
|
||||
// callback_context when the handler was created. |succeeded| indicates
|
||||
// whether a minidump file was successfully written.
|
||||
//
|
||||
// If an exception occurred and the callback returns true, Breakpad will
|
||||
// treat the exception as fully-handled, suppressing any other handlers from
|
||||
@ -103,124 +100,179 @@ class ExceptionHandler {
|
||||
// should normally return the value of |succeeded|, or when they wish to
|
||||
// not report an exception of handled, false. Callbacks will rarely want to
|
||||
// return true directly (unless |succeeded| is true).
|
||||
typedef bool (*MinidumpCallback)(const char *dump_path,
|
||||
const char *minidump_id,
|
||||
void *context,
|
||||
typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
|
||||
void* context,
|
||||
bool succeeded);
|
||||
|
||||
// In certain cases, a user may wish to handle the generation of the minidump
|
||||
// themselves. In this case, they can install a handler callback which is
|
||||
// called when a crash has occurred. If this function returns true, no other
|
||||
// processing of occurs and the process will shortly be crashed. If this
|
||||
// returns false, the normal processing continues.
|
||||
typedef bool (*HandlerCallback)(const void* crash_context,
|
||||
size_t crash_context_size,
|
||||
void* context);
|
||||
|
||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
||||
// Before writing a minidump, the optional filter callback will be called.
|
||||
// Before writing a minidump, the optional |filter| callback will be called.
|
||||
// Its return value determines whether or not Breakpad should write a
|
||||
// minidump. Minidump files will be written to dump_path, and the optional
|
||||
// callback is called after writing the dump file, as described above.
|
||||
// minidump. The minidump content will be written to the file path or file
|
||||
// descriptor from |descriptor|, and the optional |callback| is called after
|
||||
// writing the dump file, as described above.
|
||||
// If install_handler is true, then a minidump will be written whenever
|
||||
// an unhandled exception occurs. If it is false, minidumps will only
|
||||
// be written when WriteMinidump is called.
|
||||
ExceptionHandler(const string &dump_path,
|
||||
FilterCallback filter, MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler);
|
||||
// If |server_fd| is valid, the minidump is generated out-of-process. If it
|
||||
// is -1, in-process generation will always be used.
|
||||
ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const int server_fd);
|
||||
~ExceptionHandler();
|
||||
|
||||
// Get and set the minidump path.
|
||||
string dump_path() const { return dump_path_; }
|
||||
void set_dump_path(const string &dump_path) {
|
||||
dump_path_ = dump_path;
|
||||
dump_path_c_ = dump_path_.c_str();
|
||||
UpdateNextID();
|
||||
const MinidumpDescriptor& minidump_descriptor() const {
|
||||
return minidump_descriptor_;
|
||||
}
|
||||
|
||||
// Writes a minidump immediately. This can be used to capture the
|
||||
// execution state independently of a crash. Returns true on success.
|
||||
void set_minidump_descriptor(const MinidumpDescriptor& descriptor) {
|
||||
minidump_descriptor_ = descriptor;
|
||||
}
|
||||
|
||||
void set_crash_handler(HandlerCallback callback) {
|
||||
crash_handler_ = callback;
|
||||
}
|
||||
|
||||
void set_crash_generation_client(CrashGenerationClient* client) {
|
||||
crash_generation_client_.reset(client);
|
||||
}
|
||||
|
||||
// Writes a minidump immediately. This can be used to capture the execution
|
||||
// state independently of a crash.
|
||||
// Returns true on success.
|
||||
// If the ExceptionHandler has been created with a path, a new file is
|
||||
// generated for each minidump. The file path can be retrieved in the
|
||||
// MinidumpDescriptor passed to the MinidumpCallback or by accessing the
|
||||
// MinidumpDescriptor directly from the ExceptionHandler (with
|
||||
// minidump_descriptor()).
|
||||
// If the ExceptionHandler has been created with a file descriptor, the file
|
||||
// descriptor is repositioned to its beginning and the previous generated
|
||||
// minidump is overwritten.
|
||||
// Note that this method is not supposed to be called from a compromised
|
||||
// context as it uses the heap.
|
||||
bool WriteMinidump();
|
||||
|
||||
// Convenience form of WriteMinidump which does not require an
|
||||
// ExceptionHandler instance.
|
||||
static bool WriteMinidump(const string &dump_path,
|
||||
static bool WriteMinidump(const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context);
|
||||
void* callback_context);
|
||||
|
||||
// Write a minidump of |child| immediately. This can be used to
|
||||
// capture the execution state of |child| independently of a crash.
|
||||
// Pass a meaningful |child_blamed_thread| to make that thread in
|
||||
// the child process the one from which a crash signature is
|
||||
// extracted.
|
||||
//
|
||||
// WARNING: the return of this function *must* happen before
|
||||
// the code that will eventually reap |child| executes.
|
||||
// Otherwise there's a pernicious race condition in which |child|
|
||||
// exits, is reaped, another process created with its pid, then that
|
||||
// new process dumped.
|
||||
static bool WriteMinidumpForChild(pid_t child,
|
||||
pid_t child_blamed_thread,
|
||||
const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context);
|
||||
|
||||
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
|
||||
// blob. It shouldn't be needed in any user code.
|
||||
struct CrashContext {
|
||||
siginfo_t siginfo;
|
||||
pid_t tid; // the crashing thread.
|
||||
struct ucontext context;
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
||||
// In case of MIPS Linux FP state is already part of struct
|
||||
// ucontext so 'float_state' is not required.
|
||||
fpstate_t float_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Returns whether out-of-process dump generation is used or not.
|
||||
bool IsOutOfProcess() const {
|
||||
return crash_generation_client_.get() != NULL;
|
||||
}
|
||||
|
||||
// Add information about a memory mapping. This can be used if
|
||||
// a custom library loader is used that maps things in a way
|
||||
// that the linux dumper can't handle by reading the maps file.
|
||||
void AddMappingInfo(const string& name,
|
||||
const uint8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
size_t file_offset);
|
||||
|
||||
// Register a block of memory of length bytes starting at address ptr
|
||||
// to be copied to the minidump when a crash happens.
|
||||
void RegisterAppMemory(void* ptr, size_t length);
|
||||
|
||||
// Unregister a block of memory that was registered with RegisterAppMemory.
|
||||
void UnregisterAppMemory(void* ptr);
|
||||
|
||||
// Force signal handling for the specified signal.
|
||||
bool SimulateSignalDelivery(int sig);
|
||||
|
||||
// Report a crash signal from an SA_SIGINFO signal handler.
|
||||
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
||||
|
||||
private:
|
||||
// Setup crash handler.
|
||||
void SetupHandler();
|
||||
// Setup signal handler for a signal.
|
||||
void SetupHandler(int signo);
|
||||
// Teardown the handler for a signal.
|
||||
void TeardownHandler(int signo);
|
||||
// Teardown the handler for a signal.
|
||||
void TeardownHandler(int signo, struct sigaction *old);
|
||||
// Teardown all handlers.
|
||||
void TeardownAllHandler();
|
||||
// Save the old signal handlers and install new ones.
|
||||
static bool InstallHandlersLocked();
|
||||
// Restore the old signal handlers.
|
||||
static void RestoreHandlersLocked();
|
||||
|
||||
// Signal handler.
|
||||
static void HandleException(int signo);
|
||||
void PreresolveSymbols();
|
||||
bool GenerateDump(CrashContext *context);
|
||||
void SendContinueSignalToChild();
|
||||
void WaitForContinueSignal();
|
||||
|
||||
// If called from a signal handler, sighandler_ebp is the ebp of
|
||||
// that signal handler's frame, and sig_ctx is an out parameter
|
||||
// that will be set to point at the sigcontext that was placed
|
||||
// on the stack by the kernel. You can pass zero and NULL
|
||||
// for the second and third parameters if you are not calling
|
||||
// this from a signal handler.
|
||||
bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp,
|
||||
struct sigcontext **sig_ctx);
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
||||
static int ThreadEntry(void* arg);
|
||||
bool DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size);
|
||||
|
||||
// Generates a new ID and stores it in next_minidump_id, and stores the
|
||||
// path of the next minidump to be written in next_minidump_path_.
|
||||
void UpdateNextID();
|
||||
const FilterCallback filter_;
|
||||
const MinidumpCallback callback_;
|
||||
void* const callback_context_;
|
||||
|
||||
private:
|
||||
FilterCallback filter_;
|
||||
MinidumpCallback callback_;
|
||||
void *callback_context_;
|
||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||
|
||||
// The directory in which a minidump will be written, set by the dump_path
|
||||
// argument to the constructor, or set_dump_path.
|
||||
string dump_path_;
|
||||
MinidumpDescriptor minidump_descriptor_;
|
||||
|
||||
// The basename of the next minidump to be written, without the extension
|
||||
string next_minidump_id_;
|
||||
// Must be volatile. The compiler is unaware of the code which runs in
|
||||
// the signal handler which reads this variable. Without volatile the
|
||||
// compiler is free to optimise away writes to this variable which it
|
||||
// believes are never read.
|
||||
volatile HandlerCallback crash_handler_;
|
||||
|
||||
// The full pathname of the next minidump to be written, including the file
|
||||
// extension
|
||||
string next_minidump_path_;
|
||||
// We need to explicitly enable ptrace of parent processes on some
|
||||
// kernels, but we need to know the PID of the cloned process before we
|
||||
// can do this. We create a pipe which we can use to block the
|
||||
// cloned process after creating it, until we have explicitly enabled
|
||||
// ptrace. This is used to store the file descriptors for the pipe
|
||||
int fdes[2];
|
||||
|
||||
// Pointers to C-string representations of the above. These are set
|
||||
// when the above are set so we can avoid calling c_str during
|
||||
// an exception.
|
||||
const char *dump_path_c_;
|
||||
const char *next_minidump_id_c_;
|
||||
const char *next_minidump_path_c_;
|
||||
// Callers can add extra info about mappings for cases where the
|
||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||
MappingList mapping_list_;
|
||||
|
||||
// True if the ExceptionHandler installed an unhandled exception filter
|
||||
// when created (with an install_handler parameter set to true).
|
||||
bool installed_handler_;
|
||||
|
||||
// The global exception handler stack. This is need becuase there may exist
|
||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||
// registered in this stack.
|
||||
static std::vector<ExceptionHandler *> *handler_stack_;
|
||||
// The index of the handler that should handle the next exception.
|
||||
static int handler_stack_index_;
|
||||
static pthread_mutex_t handler_stack_mutex_;
|
||||
|
||||
// The minidump generator.
|
||||
MinidumpGenerator minidump_generator_;
|
||||
|
||||
// disallow copy ctor and operator=
|
||||
explicit ExceptionHandler(const ExceptionHandler &);
|
||||
void operator=(const ExceptionHandler &);
|
||||
|
||||
// The sigactions structure we use for each signal
|
||||
struct sigaction act_;
|
||||
|
||||
|
||||
// Keep the previous handlers for the signal.
|
||||
// We're wasting a bit of memory here since we only change
|
||||
// the handler for some signals but i want to avoid allocating
|
||||
// memory in the signal handler
|
||||
struct sigaction old_actions_[NSIG];
|
||||
// Callers can request additional memory regions to be included in
|
||||
// the dump.
|
||||
AppMemoryList app_memory_list_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__
|
||||
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
|
@ -1,124 +0,0 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Li Liu
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/linux_thread.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
// Thread use this to see if it should stop working.
|
||||
static bool should_exit = false;
|
||||
|
||||
static int foo2(int arg) {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
/*DDDebug*/printf("%s:%d\n", __FUNCTION__, __LINE__);
|
||||
int c = 0xcccccccc;
|
||||
fprintf(stderr, "Thread trying to crash: %x\n", getpid());
|
||||
c = *reinterpret_cast<int *>(0x5);
|
||||
return c;
|
||||
}
|
||||
|
||||
static int foo(int arg) {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
int b = 0xbbbbbbbb;
|
||||
b = foo2(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
static void *thread_crash(void *) {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
int a = 0xaaaaaaaa;
|
||||
sleep(1);
|
||||
a = foo(a);
|
||||
printf("%x\n", a);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *thread_main(void *) {
|
||||
while (!should_exit)
|
||||
sleep(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void CreateCrashThread() {
|
||||
pthread_t h;
|
||||
pthread_create(&h, NULL, thread_crash, NULL);
|
||||
pthread_detach(h);
|
||||
}
|
||||
|
||||
// Create working threads.
|
||||
static void CreateThread(int num) {
|
||||
pthread_t h;
|
||||
for (int i = 0; i < num; ++i) {
|
||||
pthread_create(&h, NULL, thread_main, NULL);
|
||||
pthread_detach(h);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback when minidump written.
|
||||
static bool MinidumpCallback(const char *dump_path,
|
||||
const char *minidump_id,
|
||||
void *context,
|
||||
bool succeeded) {
|
||||
int index = reinterpret_cast<int>(context);
|
||||
printf("%d %s: %s is dumped\n", index, __FUNCTION__, minidump_id);
|
||||
if (index == 0) {
|
||||
should_exit = true;
|
||||
return true;
|
||||
}
|
||||
// Don't process it.
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int handler_index = 0;
|
||||
ExceptionHandler handler_ignore(".", NULL, MinidumpCallback,
|
||||
(void*)handler_index, true);
|
||||
++handler_index;
|
||||
ExceptionHandler handler_process(".", NULL, MinidumpCallback,
|
||||
(void*)handler_index, true);
|
||||
CreateCrashThread();
|
||||
CreateThread(10);
|
||||
|
||||
while (true)
|
||||
sleep(1);
|
||||
should_exit = true;
|
||||
|
||||
return 0;
|
||||
}
|
1179
src/client/linux/handler/exception_handler_unittest.cc
Normal file
1179
src/client/linux/handler/exception_handler_unittest.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,411 +0,0 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Li Liu
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
|
||||
#include "client/linux/handler/linux_thread.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
// This unamed namespace contains helper function.
|
||||
namespace {
|
||||
|
||||
// Context information for the callbacks when validating address by listing
|
||||
// modules.
|
||||
struct AddressValidatingContext {
|
||||
uintptr_t address;
|
||||
bool is_mapped;
|
||||
|
||||
AddressValidatingContext() : address(0UL), is_mapped(false) {
|
||||
}
|
||||
};
|
||||
|
||||
// Convert from string to int.
|
||||
bool LocalAtoi(char *s, int *r) {
|
||||
assert(s != NULL);
|
||||
assert(r != NULL);
|
||||
char *endptr = NULL;
|
||||
int ret = strtol(s, &endptr, 10);
|
||||
if (endptr == s)
|
||||
return false;
|
||||
*r = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fill the proc path of a thread given its id.
|
||||
void FillProcPath(int pid, char *path, int path_size) {
|
||||
char pid_str[32];
|
||||
snprintf(pid_str, sizeof(pid_str), "%d", pid);
|
||||
snprintf(path, path_size, "/proc/%s/", pid_str);
|
||||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
bool ReadThreadInfo(int pid, ThreadInfo *info) {
|
||||
assert(info != NULL);
|
||||
char status_path[80];
|
||||
// Max size we want to read from status file.
|
||||
static const int kStatusMaxSize = 1024;
|
||||
char status_content[kStatusMaxSize];
|
||||
|
||||
FillProcPath(pid, status_path, sizeof(status_path));
|
||||
strcat(status_path, "status");
|
||||
int fd = open(status_path, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
int num_read = read(fd, status_content, kStatusMaxSize - 1);
|
||||
if (num_read < 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
status_content[num_read] = '\0';
|
||||
|
||||
char *tgid_start = strstr(status_content, "Tgid:");
|
||||
if (tgid_start)
|
||||
sscanf(tgid_start, "Tgid:\t%d\n", &(info->tgid));
|
||||
else
|
||||
// tgid not supported by kernel??
|
||||
info->tgid = 0;
|
||||
|
||||
tgid_start = strstr(status_content, "Pid:");
|
||||
if (tgid_start) {
|
||||
sscanf(tgid_start, "Pid:\t%d\n" "PPid:\t%d\n", &(info->pid),
|
||||
&(info->ppid));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback invoked for each mapped module.
|
||||
// It use the module's adderss range to validate the address.
|
||||
bool IsAddressInModuleCallback(const ModuleInfo &module_info,
|
||||
void *context) {
|
||||
AddressValidatingContext *addr =
|
||||
reinterpret_cast<AddressValidatingContext *>(context);
|
||||
addr->is_mapped = ((addr->address >= module_info.start_addr) &&
|
||||
(addr->address <= module_info.start_addr +
|
||||
module_info.size));
|
||||
return !addr->is_mapped;
|
||||
}
|
||||
|
||||
#if defined(__i386__) && !defined(NO_FRAME_POINTER)
|
||||
void *GetNextFrame(void **last_ebp) {
|
||||
void *sp = *last_ebp;
|
||||
if ((unsigned long)sp == (unsigned long)last_ebp)
|
||||
return NULL;
|
||||
if ((unsigned long)sp & (sizeof(void *) - 1))
|
||||
return NULL;
|
||||
if ((unsigned long)sp - (unsigned long)last_ebp > 100000)
|
||||
return NULL;
|
||||
return sp;
|
||||
}
|
||||
#else
|
||||
void *GetNextFrame(void **last_ebp) {
|
||||
return reinterpret_cast<void*>(last_ebp);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Suspend a thread by attaching to it.
|
||||
bool SuspendThread(int pid, void *context) {
|
||||
// This may fail if the thread has just died or debugged.
|
||||
errno = 0;
|
||||
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
|
||||
errno != 0) {
|
||||
return false;
|
||||
}
|
||||
while (waitpid(pid, NULL, __WALL) < 0) {
|
||||
if (errno != EINTR) {
|
||||
ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resume a thread by detaching from it.
|
||||
bool ResumeThread(int pid, void *context) {
|
||||
return ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
|
||||
}
|
||||
|
||||
// Callback to get the thread information.
|
||||
// Will be called for each thread found.
|
||||
bool ThreadInfoCallback(int pid, void *context) {
|
||||
CallbackParam<ThreadCallback> *thread_callback =
|
||||
reinterpret_cast<CallbackParam<ThreadCallback> *>(context);
|
||||
ThreadInfo thread_info;
|
||||
if (ReadThreadInfo(pid, &thread_info) && thread_callback) {
|
||||
// Invoke callback from caller.
|
||||
return (thread_callback->call_back)(thread_info, thread_callback->context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
LinuxThread::LinuxThread(int pid) : pid_(pid) , threads_suspened_(false) {
|
||||
}
|
||||
|
||||
LinuxThread::~LinuxThread() {
|
||||
if (threads_suspened_)
|
||||
ResumeAllThreads();
|
||||
}
|
||||
|
||||
int LinuxThread::SuspendAllThreads() {
|
||||
CallbackParam<PidCallback> callback_param(SuspendThread, NULL);
|
||||
int thread_count = 0;
|
||||
if ((thread_count = IterateProcSelfTask(pid_, &callback_param)) > 0)
|
||||
threads_suspened_ = true;
|
||||
return thread_count;
|
||||
}
|
||||
|
||||
void LinuxThread::ResumeAllThreads() const {
|
||||
CallbackParam<PidCallback> callback_param(ResumeThread, NULL);
|
||||
IterateProcSelfTask(pid_, &callback_param);
|
||||
}
|
||||
|
||||
int LinuxThread::GetThreadCount() const {
|
||||
return IterateProcSelfTask(pid_, NULL);
|
||||
}
|
||||
|
||||
int LinuxThread::ListThreads(
|
||||
CallbackParam<ThreadCallback> *thread_callback_param) const {
|
||||
CallbackParam<PidCallback> callback_param(ThreadInfoCallback,
|
||||
thread_callback_param);
|
||||
return IterateProcSelfTask(pid_, &callback_param);
|
||||
}
|
||||
|
||||
bool LinuxThread::GetRegisters(int pid, user_regs_struct *regs) const {
|
||||
assert(regs);
|
||||
return (regs != NULL &&
|
||||
(ptrace(PTRACE_GETREGS, pid, NULL, regs) == 0) &&
|
||||
errno == 0);
|
||||
}
|
||||
|
||||
// Get the floating-point registers of a thread.
|
||||
// The caller must get the thread pid by ListThreads.
|
||||
bool LinuxThread::GetFPRegisters(int pid, user_fpregs_struct *regs) const {
|
||||
assert(regs);
|
||||
return (regs != NULL &&
|
||||
(ptrace(PTRACE_GETREGS, pid, NULL, regs) ==0) &&
|
||||
errno == 0);
|
||||
}
|
||||
|
||||
bool LinuxThread::GetFPXRegisters(int pid, user_fpxregs_struct *regs) const {
|
||||
assert(regs);
|
||||
return (regs != NULL &&
|
||||
(ptrace(PTRACE_GETFPREGS, pid, NULL, regs) != 0) &&
|
||||
errno == 0);
|
||||
}
|
||||
|
||||
bool LinuxThread::GetDebugRegisters(int pid, DebugRegs *regs) const {
|
||||
assert(regs);
|
||||
|
||||
#define GET_DR(name, num)\
|
||||
name->dr##num = ptrace(PTRACE_PEEKUSER, pid,\
|
||||
offsetof(struct user, u_debugreg[num]), NULL)
|
||||
GET_DR(regs, 0);
|
||||
GET_DR(regs, 1);
|
||||
GET_DR(regs, 2);
|
||||
GET_DR(regs, 3);
|
||||
GET_DR(regs, 4);
|
||||
GET_DR(regs, 5);
|
||||
GET_DR(regs, 6);
|
||||
GET_DR(regs, 7);
|
||||
return true;
|
||||
}
|
||||
|
||||
int LinuxThread::GetThreadStackDump(uintptr_t current_ebp,
|
||||
uintptr_t current_esp,
|
||||
void *buf,
|
||||
int buf_size) const {
|
||||
assert(buf);
|
||||
assert(buf_size > 0);
|
||||
|
||||
uintptr_t stack_bottom = GetThreadStackBottom(current_ebp);
|
||||
int size = stack_bottom - current_esp;
|
||||
size = buf_size > size ? size : buf_size;
|
||||
if (size > 0)
|
||||
memcpy(buf, reinterpret_cast<void*>(current_esp), size);
|
||||
return size;
|
||||
}
|
||||
|
||||
// Get the stack bottom of a thread by stack walking. It works
|
||||
// unless the stack has been corrupted or the frame pointer has been omited.
|
||||
// This is just a temporary solution before we get better ideas about how
|
||||
// this can be done.
|
||||
//
|
||||
// We will check each frame address by checking into module maps.
|
||||
// TODO(liuli): Improve it.
|
||||
uintptr_t LinuxThread::GetThreadStackBottom(uintptr_t current_ebp) const {
|
||||
void **sp = reinterpret_cast<void **>(current_ebp);
|
||||
void **previous_sp = sp;
|
||||
while (sp && IsAddressMapped((uintptr_t)sp)) {
|
||||
previous_sp = sp;
|
||||
sp = reinterpret_cast<void **>(GetNextFrame(sp));
|
||||
}
|
||||
return (uintptr_t)previous_sp;
|
||||
}
|
||||
|
||||
int LinuxThread::GetModuleCount() const {
|
||||
return ListModules(NULL);
|
||||
}
|
||||
|
||||
int LinuxThread::ListModules(
|
||||
CallbackParam<ModuleCallback> *callback_param) const {
|
||||
char line[512];
|
||||
const char *maps_path = "/proc/self/maps";
|
||||
|
||||
int module_count = 0;
|
||||
FILE *fp = fopen(maps_path, "r");
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
uintptr_t start_addr;
|
||||
uintptr_t end_addr;
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (sscanf(line, "%x-%x", &start_addr, &end_addr) == 2) {
|
||||
ModuleInfo module;
|
||||
memset(&module, 0, sizeof(module));
|
||||
module.start_addr = start_addr;
|
||||
module.size = end_addr - start_addr;
|
||||
char *name = NULL;
|
||||
assert(module.size > 0);
|
||||
// Only copy name if the name is a valid path name.
|
||||
if ((name = strchr(line, '/')) != NULL) {
|
||||
// Get rid of the last '\n' in line
|
||||
char *last_return = strchr(line, '\n');
|
||||
if (last_return != NULL)
|
||||
*last_return = '\0';
|
||||
// Keep a space for the ending 0.
|
||||
strncpy(module.name, name, sizeof(module.name) - 1);
|
||||
++module_count;
|
||||
}
|
||||
if (callback_param &&
|
||||
!(callback_param->call_back(module, callback_param->context)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return module_count;
|
||||
}
|
||||
|
||||
// Parse /proc/$pid/tasks to list all the threads of the process identified by
|
||||
// pid.
|
||||
int LinuxThread::IterateProcSelfTask(int pid,
|
||||
CallbackParam<PidCallback> *callback_param) const {
|
||||
char task_path[80];
|
||||
FillProcPath(pid, task_path, sizeof(task_path));
|
||||
strcat(task_path, "task");
|
||||
|
||||
DIR *dir = opendir(task_path);
|
||||
if (dir == NULL)
|
||||
return -1;
|
||||
|
||||
int pid_number = 0;
|
||||
// Record the last pid we've found. This is used for duplicated thread
|
||||
// removal. Duplicated thread information can be found in /proc/$pid/tasks.
|
||||
int last_pid = -1;
|
||||
struct dirent *entry = NULL;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") &&
|
||||
strcmp(entry->d_name, "..")) {
|
||||
int tpid = 0;
|
||||
if (LocalAtoi(entry->d_name, &tpid) &&
|
||||
last_pid != tpid) {
|
||||
last_pid = tpid;
|
||||
++pid_number;
|
||||
// Invoke the callback.
|
||||
if (callback_param &&
|
||||
!(callback_param->call_back)(tpid, callback_param->context))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return pid_number;
|
||||
}
|
||||
|
||||
// Check if the address is a valid virtual address.
|
||||
// If the address is in any of the mapped modules, we take it as valid.
|
||||
// Otherwise it is invalid.
|
||||
bool LinuxThread::IsAddressMapped(uintptr_t address) const {
|
||||
AddressValidatingContext addr;
|
||||
addr.address = address;
|
||||
CallbackParam<ModuleCallback> callback_param(IsAddressInModuleCallback,
|
||||
&addr);
|
||||
ListModules(&callback_param);
|
||||
return addr.is_mapped;
|
||||
}
|
||||
|
||||
bool LinuxThread::FindSigContext(uintptr_t sighandler_ebp,
|
||||
struct sigcontext **sig_ctx) {
|
||||
uintptr_t previous_ebp;
|
||||
const int MAX_STACK_DEPTH = 10;
|
||||
int depth_counter = 0;
|
||||
|
||||
do {
|
||||
// We're looking for a |struct sigcontext| as the second parameter
|
||||
// to a signal handler function call. Luckily, the sigcontext
|
||||
// has an ebp member which should match the ebp pointed to
|
||||
// by the ebp of the signal handler frame.
|
||||
previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame(
|
||||
reinterpret_cast<void**>(sighandler_ebp)));
|
||||
// The stack looks like this:
|
||||
// | previous ebp | previous eip | first param | second param |,
|
||||
// so we need to offset by 3 to get to the second parameter.
|
||||
*sig_ctx = reinterpret_cast<struct sigcontext*>(sighandler_ebp +
|
||||
3 * sizeof(uintptr_t));
|
||||
sighandler_ebp = previous_ebp;
|
||||
depth_counter++;
|
||||
} while(previous_ebp != (*sig_ctx)->ebp && sighandler_ebp != 0 &&
|
||||
IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH);
|
||||
|
||||
return previous_ebp == (*sig_ctx)->ebp && previous_ebp != 0;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -1,204 +0,0 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Li Liu
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
#ifndef CLIENT_LINUX_HANDLER_LINUX_THREAD_H__
|
||||
#define CLIENT_LINUX_HANDLER_LINUX_THREAD_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Max module path name length.
|
||||
#define kMaxModuleNameLength 256
|
||||
|
||||
// Holding information about a thread in the process.
|
||||
struct ThreadInfo {
|
||||
// Id of the thread group.
|
||||
int tgid;
|
||||
// Id of the thread.
|
||||
int pid;
|
||||
// Id of the parent process.
|
||||
int ppid;
|
||||
};
|
||||
|
||||
// Holding infomaton about a module in the process.
|
||||
struct ModuleInfo {
|
||||
char name[kMaxModuleNameLength];
|
||||
uintptr_t start_addr;
|
||||
int size;
|
||||
};
|
||||
|
||||
// Holding debug registers.
|
||||
struct DebugRegs {
|
||||
int dr0;
|
||||
int dr1;
|
||||
int dr2;
|
||||
int dr3;
|
||||
int dr4;
|
||||
int dr5;
|
||||
int dr6;
|
||||
int dr7;
|
||||
};
|
||||
|
||||
// A callback to run when got a thread in the process.
|
||||
// Return true will go on to the next thread while return false will stop the
|
||||
// iteration.
|
||||
typedef bool (*ThreadCallback)(const ThreadInfo &thread_info, void *context);
|
||||
|
||||
// A callback to run when a new module is found in the process.
|
||||
// Return true will go on to the next module while return false will stop the
|
||||
// iteration.
|
||||
typedef bool (*ModuleCallback)(const ModuleInfo &module_info, void *context);
|
||||
|
||||
// Holding the callback information.
|
||||
template<class CallbackFunc>
|
||||
struct CallbackParam {
|
||||
// Callback function address.
|
||||
CallbackFunc call_back;
|
||||
// Callback context;
|
||||
void *context;
|
||||
|
||||
CallbackParam() : call_back(NULL), context(NULL) {
|
||||
}
|
||||
|
||||
CallbackParam(CallbackFunc func, void *func_context) :
|
||||
call_back(func), context(func_context) {
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// LinuxThread
|
||||
//
|
||||
// Provides handy support for operation on linux threads.
|
||||
// It uses ptrace to get thread registers. Since ptrace only works in a
|
||||
// different process other than the one being ptraced, user of this class
|
||||
// should create another process before using the class.
|
||||
//
|
||||
// The process should be created in the following way:
|
||||
// int cloned_pid = clone(ProcessEntryFunction, stack_address,
|
||||
// CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
// (void*)&arguments);
|
||||
// waitpid(cloned_pid, NULL, __WALL);
|
||||
//
|
||||
// If CLONE_VM is not used, GetThreadStackBottom, GetThreadStackDump
|
||||
// will not work since it just use memcpy to get the stack dump.
|
||||
//
|
||||
class LinuxThread {
|
||||
public:
|
||||
// Create a LinuxThread instance to list all the threads in a process.
|
||||
explicit LinuxThread(int pid);
|
||||
~LinuxThread();
|
||||
|
||||
// Stop all the threads in the process.
|
||||
// Return the number of stopped threads in the process.
|
||||
// Return -1 means failed to stop threads.
|
||||
int SuspendAllThreads();
|
||||
|
||||
// Resume all the suspended threads.
|
||||
void ResumeAllThreads() const;
|
||||
|
||||
// Get the count of threads in the process.
|
||||
// Return -1 means error.
|
||||
int GetThreadCount() const;
|
||||
|
||||
// List the threads of process.
|
||||
// Whenever there is a thread found, the callback will be invoked to process
|
||||
// the information.
|
||||
// Return number of threads listed.
|
||||
int ListThreads(CallbackParam<ThreadCallback> *thread_callback_param) const;
|
||||
|
||||
// Get the general purpose registers of a thread.
|
||||
// The caller must get the thread pid by ListThreads.
|
||||
bool GetRegisters(int pid, user_regs_struct *regs) const;
|
||||
|
||||
// Get the floating-point registers of a thread.
|
||||
// The caller must get the thread pid by ListThreads.
|
||||
bool GetFPRegisters(int pid, user_fpregs_struct *regs) const;
|
||||
|
||||
// Get all the extended floating-point registers. May not work on all
|
||||
// machines.
|
||||
// The caller must get the thread pid by ListThreads.
|
||||
bool GetFPXRegisters(int pid, user_fpxregs_struct *regs) const;
|
||||
|
||||
// Get the debug registers.
|
||||
// The caller must get the thread pid by ListThreads.
|
||||
bool GetDebugRegisters(int pid, DebugRegs *regs) const;
|
||||
|
||||
// Get the stack memory dump.
|
||||
int GetThreadStackDump(uintptr_t current_ebp,
|
||||
uintptr_t current_esp,
|
||||
void *buf,
|
||||
int buf_size) const;
|
||||
|
||||
// Get the module count of the current process.
|
||||
int GetModuleCount() const;
|
||||
|
||||
// Get the mapped modules in the address space.
|
||||
// Whenever a module is found, the callback will be invoked to process the
|
||||
// information.
|
||||
// Return how may modules are found.
|
||||
int ListModules(CallbackParam<ModuleCallback> *callback_param) const;
|
||||
|
||||
// Get the bottom of the stack from ebp.
|
||||
uintptr_t GetThreadStackBottom(uintptr_t current_ebp) const;
|
||||
|
||||
// Finds a sigcontext on the stack given the ebp of our signal handler.
|
||||
bool FindSigContext(uintptr_t sighandler_ebp, struct sigcontext **sig_ctx);
|
||||
|
||||
private:
|
||||
// This callback will run when a new thread has been found.
|
||||
typedef bool (*PidCallback)(int pid, void *context);
|
||||
|
||||
// Read thread information from /proc/$pid/task.
|
||||
// Whenever a thread has been found, and callback will be invoked with
|
||||
// the pid of the thread.
|
||||
// Return number of threads found.
|
||||
// Return -1 means the directory doesn't exist.
|
||||
int IterateProcSelfTask(int pid,
|
||||
CallbackParam<PidCallback> *callback_param) const;
|
||||
|
||||
// Check if the address is a valid virtual address.
|
||||
bool IsAddressMapped(uintptr_t address) const;
|
||||
|
||||
private:
|
||||
// The pid of the process we are listing threads.
|
||||
int pid_;
|
||||
|
||||
// Mark if we have suspended the threads.
|
||||
bool threads_suspened_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_LINUX_THREAD_H__
|
@ -1,224 +0,0 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Li Liu
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "client/linux/handler/linux_thread.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
// Thread use this to see if it should stop working.
|
||||
static bool should_exit = false;
|
||||
|
||||
static void foo2(int *a) {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
int c = 0xcccccccc;
|
||||
c = c;
|
||||
while (!should_exit)
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
static void foo() {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
int a = 0xaaaaaaaa;
|
||||
foo2(&a);
|
||||
}
|
||||
|
||||
static void *thread_main(void *) {
|
||||
// Stack variable, used for debugging stack dumps.
|
||||
int b = 0xbbbbbbbb;
|
||||
b = b;
|
||||
while (!should_exit) {
|
||||
foo();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void CreateThreads(int num) {
|
||||
pthread_t handle;
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (0 != pthread_create(&handle, NULL, thread_main, NULL))
|
||||
fprintf(stderr, "Failed to create thread.\n");
|
||||
else
|
||||
pthread_detach(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ProcessOneModule(const struct ModuleInfo &module_info,
|
||||
void *context) {
|
||||
printf("0x%x[%8d] %s\n", module_info.start_addr, module_info.size,
|
||||
module_info.name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ProcessOneThread(const struct ThreadInfo &thread_info,
|
||||
void *context) {
|
||||
printf("\n\nPID: %d, TGID: %d, PPID: %d\n",
|
||||
thread_info.pid,
|
||||
thread_info.tgid,
|
||||
thread_info.ppid);
|
||||
|
||||
struct user_regs_struct regs;
|
||||
struct user_fpregs_struct fp_regs;
|
||||
struct user_fpxregs_struct fpx_regs;
|
||||
struct DebugRegs dbg_regs;
|
||||
|
||||
LinuxThread *threads = reinterpret_cast<LinuxThread *>(context);
|
||||
memset(®s, 0, sizeof(regs));
|
||||
if (threads->GetRegisters(thread_info.pid, ®s)) {
|
||||
printf(" gs = 0x%lx\n", regs.xgs);
|
||||
printf(" fs = 0x%lx\n", regs.xfs);
|
||||
printf(" es = 0x%lx\n", regs.xes);
|
||||
printf(" ds = 0x%lx\n", regs.xds);
|
||||
printf(" edi = 0x%lx\n", regs.edi);
|
||||
printf(" esi = 0x%lx\n", regs.esi);
|
||||
printf(" ebx = 0x%lx\n", regs.ebx);
|
||||
printf(" edx = 0x%lx\n", regs.edx);
|
||||
printf(" ecx = 0x%lx\n", regs.ecx);
|
||||
printf(" eax = 0x%lx\n", regs.eax);
|
||||
printf(" ebp = 0x%lx\n", regs.ebp);
|
||||
printf(" eip = 0x%lx\n", regs.eip);
|
||||
printf(" cs = 0x%lx\n", regs.xcs);
|
||||
printf(" eflags = 0x%lx\n", regs.eflags);
|
||||
printf(" esp = 0x%lx\n", regs.esp);
|
||||
printf(" ss = 0x%lx\n", regs.xss);
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Failed to get general purpose registers\n");
|
||||
}
|
||||
memset(&fp_regs, 0, sizeof(fp_regs));
|
||||
if (threads->GetFPRegisters(thread_info.pid, &fp_regs)) {
|
||||
printf("\n Floating point registers:\n");
|
||||
printf(" fctl = 0x%lx\n", fp_regs.cwd);
|
||||
printf(" fstat = 0x%lx\n", fp_regs.swd);
|
||||
printf(" ftag = 0x%lx\n", fp_regs.twd);
|
||||
printf(" fioff = 0x%lx\n", fp_regs.fip);
|
||||
printf(" fiseg = 0x%lx\n", fp_regs.fcs);
|
||||
printf(" fooff = 0x%lx\n", fp_regs.foo);
|
||||
printf(" foseg = 0x%lx\n", fp_regs.fos);
|
||||
int st_space_size = sizeof(fp_regs.st_space) / sizeof(fp_regs.st_space[0]);
|
||||
printf(" st_space[%2d] = 0x", st_space_size);
|
||||
for (int i = 0; i < st_space_size; ++i)
|
||||
printf("%02lx", fp_regs.st_space[i]);
|
||||
printf("\n");
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Failed to get floating-point registers\n");
|
||||
}
|
||||
memset(&fpx_regs, 0, sizeof(fpx_regs));
|
||||
if (threads->GetFPXRegisters(thread_info.pid, &fpx_regs)) {
|
||||
printf("\n Extended floating point registers:\n");
|
||||
printf(" fctl = 0x%x\n", fpx_regs.cwd);
|
||||
printf(" fstat = 0x%x\n", fpx_regs.swd);
|
||||
printf(" ftag = 0x%x\n", fpx_regs.twd);
|
||||
printf(" fioff = 0x%lx\n", fpx_regs.fip);
|
||||
printf(" fiseg = 0x%lx\n", fpx_regs.fcs);
|
||||
printf(" fooff = 0x%lx\n", fpx_regs.foo);
|
||||
printf(" foseg = 0x%lx\n", fpx_regs.fos);
|
||||
printf(" fop = 0x%x\n", fpx_regs.fop);
|
||||
printf(" mxcsr = 0x%lx\n", fpx_regs.mxcsr);
|
||||
int space_size = sizeof(fpx_regs.st_space) / sizeof(fpx_regs.st_space[0]);
|
||||
printf(" st_space[%2d] = 0x", space_size);
|
||||
for (int i = 0; i < space_size; ++i)
|
||||
printf("%02lx", fpx_regs.st_space[i]);
|
||||
printf("\n");
|
||||
space_size = sizeof(fpx_regs.xmm_space) / sizeof(fpx_regs.xmm_space[0]);
|
||||
printf(" xmm_space[%2d] = 0x", space_size);
|
||||
for (int i = 0; i < space_size; ++i)
|
||||
printf("%02lx", fpx_regs.xmm_space[i]);
|
||||
printf("\n");
|
||||
}
|
||||
if (threads->GetDebugRegisters(thread_info.pid, &dbg_regs)) {
|
||||
printf("\n Debug registers:\n");
|
||||
printf(" dr0 = 0x%x\n", dbg_regs.dr0);
|
||||
printf(" dr1 = 0x%x\n", dbg_regs.dr1);
|
||||
printf(" dr2 = 0x%x\n", dbg_regs.dr2);
|
||||
printf(" dr3 = 0x%x\n", dbg_regs.dr3);
|
||||
printf(" dr4 = 0x%x\n", dbg_regs.dr4);
|
||||
printf(" dr5 = 0x%x\n", dbg_regs.dr5);
|
||||
printf(" dr6 = 0x%x\n", dbg_regs.dr6);
|
||||
printf(" dr7 = 0x%x\n", dbg_regs.dr7);
|
||||
printf("\n");
|
||||
}
|
||||
if (regs.esp != 0) {
|
||||
// Print the stack content.
|
||||
int size = 1024 * 2;
|
||||
char *buf = new char[size];
|
||||
size = threads->GetThreadStackDump(regs.ebp,
|
||||
regs.esp,
|
||||
(void*)buf, size);
|
||||
printf(" Stack content: = 0x");
|
||||
size /= sizeof(unsigned long);
|
||||
unsigned long *p_buf = (unsigned long *)(buf);
|
||||
for (int i = 0; i < size; i += 1)
|
||||
printf("%.8lx ", p_buf[i]);
|
||||
delete []buf;
|
||||
printf("\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int PrintAllThreads(void *argument) {
|
||||
int pid = (int)argument;
|
||||
|
||||
LinuxThread threads(pid);
|
||||
int total_thread = threads.SuspendAllThreads();
|
||||
printf("There are %d threads in the process: %d\n", total_thread, pid);
|
||||
int total_module = threads.GetModuleCount();
|
||||
printf("There are %d modules in the process: %d\n", total_module, pid);
|
||||
CallbackParam<ModuleCallback> module_callback(ProcessOneModule, &threads);
|
||||
threads.ListModules(&module_callback);
|
||||
CallbackParam<ThreadCallback> thread_callback(ProcessOneThread, &threads);
|
||||
threads.ListThreads(&thread_callback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int pid = getpid();
|
||||
printf("Main thread is %d\n", pid);
|
||||
CreateThreads(1);
|
||||
// Create stack for the process.
|
||||
char *stack = new char[1024 * 100];
|
||||
int cloned_pid = clone(PrintAllThreads, stack + 1024 * 100,
|
||||
CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
(void*)getpid());
|
||||
waitpid(cloned_pid, NULL, __WALL);
|
||||
should_exit = true;
|
||||
printf("Test finished.\n");
|
||||
|
||||
delete []stack;
|
||||
return 0;
|
||||
}
|
48
src/client/linux/handler/microdump_extra_info.h
Normal file
48
src/client/linux/handler/microdump_extra_info.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2015 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
||||
#define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct MicrodumpExtraInfo {
|
||||
// Strings pointed to by this struct are not copied, and are
|
||||
// expected to remain valid for the lifetime of the process.
|
||||
const char* build_fingerprint;
|
||||
const char* product_info;
|
||||
const char* gpu_fingerprint;
|
||||
|
||||
MicrodumpExtraInfo()
|
||||
: build_fingerprint(NULL), product_info(NULL), gpu_fingerprint(NULL) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
87
src/client/linux/handler/minidump_descriptor.cc
Normal file
87
src/client/linux/handler/minidump_descriptor.cc
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2012 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//static
|
||||
const MinidumpDescriptor::MicrodumpOnConsole
|
||||
MinidumpDescriptor::kMicrodumpOnConsole = {};
|
||||
|
||||
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
||||
: mode_(descriptor.mode_),
|
||||
fd_(descriptor.fd_),
|
||||
directory_(descriptor.directory_),
|
||||
c_path_(NULL),
|
||||
size_limit_(descriptor.size_limit_),
|
||||
microdump_extra_info_(descriptor.microdump_extra_info_) {
|
||||
// The copy constructor is not allowed to be called on a MinidumpDescriptor
|
||||
// with a valid path_, as getting its c_path_ would require the heap which
|
||||
// can cause problems in compromised environments.
|
||||
assert(descriptor.path_.empty());
|
||||
}
|
||||
|
||||
MinidumpDescriptor& MinidumpDescriptor::operator=(
|
||||
const MinidumpDescriptor& descriptor) {
|
||||
assert(descriptor.path_.empty());
|
||||
|
||||
mode_ = descriptor.mode_;
|
||||
fd_ = descriptor.fd_;
|
||||
directory_ = descriptor.directory_;
|
||||
path_.clear();
|
||||
if (c_path_) {
|
||||
// This descriptor already had a path set, so generate a new one.
|
||||
c_path_ = NULL;
|
||||
UpdatePath();
|
||||
}
|
||||
size_limit_ = descriptor.size_limit_;
|
||||
microdump_extra_info_ = descriptor.microdump_extra_info_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MinidumpDescriptor::UpdatePath() {
|
||||
assert(mode_ == kWriteMinidumpToFile && !directory_.empty());
|
||||
|
||||
GUID guid;
|
||||
char guid_str[kGUIDStringLength + 1];
|
||||
if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
path_.clear();
|
||||
path_ = directory_ + "/" + guid_str + ".dmp";
|
||||
c_path_ = path_.c_str();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
149
src/client/linux/handler/minidump_descriptor.h
Normal file
149
src/client/linux/handler/minidump_descriptor.h
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2012 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
||||
#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
// This class describes how a crash dump should be generated, either:
|
||||
// - Writing a full minidump to a file in a given directory (the actual path,
|
||||
// inside the directory, is determined by this class).
|
||||
// - Writing a full minidump to a given fd.
|
||||
// - Writing a reduced microdump to the console (logcat on Android).
|
||||
namespace google_breakpad {
|
||||
|
||||
class MinidumpDescriptor {
|
||||
public:
|
||||
struct MicrodumpOnConsole {};
|
||||
static const MicrodumpOnConsole kMicrodumpOnConsole;
|
||||
|
||||
MinidumpDescriptor()
|
||||
: mode_(kUninitialized),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
|
||||
explicit MinidumpDescriptor(const string& directory)
|
||||
: mode_(kWriteMinidumpToFile),
|
||||
fd_(-1),
|
||||
directory_(directory),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
assert(!directory.empty());
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(int fd)
|
||||
: mode_(kWriteMinidumpToFd),
|
||||
fd_(fd),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
assert(fd != -1);
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
|
||||
: mode_(kWriteMicrodumpToConsole),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
|
||||
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
|
||||
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
|
||||
|
||||
static MinidumpDescriptor getMicrodumpDescriptor();
|
||||
|
||||
bool IsFD() const { return mode_ == kWriteMinidumpToFd; }
|
||||
|
||||
int fd() const { return fd_; }
|
||||
|
||||
string directory() const { return directory_; }
|
||||
|
||||
const char* path() const { return c_path_; }
|
||||
|
||||
bool IsMicrodumpOnConsole() const {
|
||||
return mode_ == kWriteMicrodumpToConsole;
|
||||
}
|
||||
|
||||
// Updates the path so it is unique.
|
||||
// Should be called from a normal context: this methods uses the heap.
|
||||
void UpdatePath();
|
||||
|
||||
off_t size_limit() const { return size_limit_; }
|
||||
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
||||
|
||||
MicrodumpExtraInfo* microdump_extra_info() {
|
||||
assert(IsMicrodumpOnConsole());
|
||||
return µdump_extra_info_;
|
||||
};
|
||||
|
||||
private:
|
||||
enum DumpMode {
|
||||
kUninitialized = 0,
|
||||
kWriteMinidumpToFile,
|
||||
kWriteMinidumpToFd,
|
||||
kWriteMicrodumpToConsole
|
||||
};
|
||||
|
||||
// Specifies the dump mode (see DumpMode).
|
||||
DumpMode mode_;
|
||||
|
||||
// The file descriptor where the minidump is generated.
|
||||
int fd_;
|
||||
|
||||
// The directory where the minidump should be generated.
|
||||
string directory_;
|
||||
|
||||
// The full path to the generated minidump.
|
||||
string path_;
|
||||
|
||||
// The C string of |path_|. Precomputed so it can be access from a compromised
|
||||
// context.
|
||||
const char* c_path_;
|
||||
|
||||
off_t size_limit_;
|
||||
|
||||
// The extra microdump data (e.g. product name/version, build
|
||||
// fingerprint, gpu fingerprint) that should be appended to the dump
|
||||
// (microdump only). Microdumps don't have the ability of appending
|
||||
// extra metadata after the dump is generated (as opposite to
|
||||
// minidumps MIME fields), therefore the extra data must be provided
|
||||
// upfront. Any memory pointed to by members of the
|
||||
// MicrodumpExtraInfo struct must be valid for the lifetime of the
|
||||
// process (read: the caller has to guarantee that it is stored in
|
||||
// global static storage.)
|
||||
MicrodumpExtraInfo microdump_extra_info_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
@ -1,816 +0,0 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Li Liu
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/file_id.h"
|
||||
#include "client/linux/handler/linux_thread.h"
|
||||
#include "client/minidump_file_writer.h"
|
||||
#include "client/minidump_file_writer-inl.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "client/linux/handler/minidump_generator.h"
|
||||
|
||||
#ifndef CLONE_UNTRACED
|
||||
#define CLONE_UNTRACED 0x00800000
|
||||
#endif
|
||||
|
||||
// This unnamed namespace contains helper functions.
|
||||
namespace {
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
// Argument for the writer function.
|
||||
struct WriterArgument {
|
||||
MinidumpFileWriter *minidump_writer;
|
||||
|
||||
// Context for the callback.
|
||||
void *version_context;
|
||||
|
||||
// Pid of the thread who called WriteMinidumpToFile
|
||||
int requester_pid;
|
||||
|
||||
// The stack bottom of the thread which caused the dump.
|
||||
// Mainly used to find the thread id of the crashed thread since signal
|
||||
// handler may not be called in the thread who caused it.
|
||||
uintptr_t crashed_stack_bottom;
|
||||
|
||||
// Pid of the crashing thread.
|
||||
int crashed_pid;
|
||||
|
||||
// Signal number when crash happed. Can be 0 if this is a requested dump.
|
||||
int signo;
|
||||
|
||||
// The ebp of the signal handler frame. Can be zero if this
|
||||
// is a requested dump.
|
||||
uintptr_t sighandler_ebp;
|
||||
|
||||
// Signal context when crash happed. Can be NULL if this is a requested dump.
|
||||
// This is actually an out parameter, but it will be filled in at the start
|
||||
// of the writer thread.
|
||||
struct sigcontext *sig_ctx;
|
||||
|
||||
// Used to get information about the threads.
|
||||
LinuxThread *thread_lister;
|
||||
};
|
||||
|
||||
// Holding context information for the callback of finding the crashing thread.
|
||||
struct FindCrashThreadContext {
|
||||
const LinuxThread *thread_lister;
|
||||
uintptr_t crashing_stack_bottom;
|
||||
int crashing_thread_pid;
|
||||
|
||||
FindCrashThreadContext() :
|
||||
thread_lister(NULL),
|
||||
crashing_stack_bottom(0UL),
|
||||
crashing_thread_pid(-1) {
|
||||
}
|
||||
};
|
||||
|
||||
// Callback for list threads.
|
||||
// It will compare the stack bottom of the provided thread with the stack
|
||||
// bottom of the crashed thread, it they are eqaul, this is thread is the one
|
||||
// who crashed.
|
||||
bool IsThreadCrashedCallback(const ThreadInfo &thread_info, void *context) {
|
||||
FindCrashThreadContext *crashing_context =
|
||||
static_cast<FindCrashThreadContext *>(context);
|
||||
const LinuxThread *thread_lister = crashing_context->thread_lister;
|
||||
struct user_regs_struct regs;
|
||||
if (thread_lister->GetRegisters(thread_info.pid, ®s)) {
|
||||
uintptr_t last_ebp = regs.ebp;
|
||||
uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp);
|
||||
if (stack_bottom > last_ebp &&
|
||||
stack_bottom == crashing_context->crashing_stack_bottom) {
|
||||
// Got it. Stop iteration.
|
||||
crashing_context->crashing_thread_pid = thread_info.pid;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the crashing thread id.
|
||||
// This is done based on stack bottom comparing.
|
||||
int FindCrashingThread(uintptr_t crashing_stack_bottom,
|
||||
int requester_pid,
|
||||
const LinuxThread *thread_lister) {
|
||||
FindCrashThreadContext context;
|
||||
context.thread_lister = thread_lister;
|
||||
context.crashing_stack_bottom = crashing_stack_bottom;
|
||||
CallbackParam<ThreadCallback> callback_param(IsThreadCrashedCallback,
|
||||
&context);
|
||||
thread_lister->ListThreads(&callback_param);
|
||||
return context.crashing_thread_pid;
|
||||
}
|
||||
|
||||
// Write the thread stack info minidump.
|
||||
bool WriteThreadStack(uintptr_t last_ebp,
|
||||
uintptr_t last_esp,
|
||||
const LinuxThread *thread_lister,
|
||||
UntypedMDRVA *memory,
|
||||
MDMemoryDescriptor *loc) {
|
||||
// Maximum stack size for a thread.
|
||||
uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp);
|
||||
if (stack_bottom > last_esp) {
|
||||
int size = stack_bottom - last_esp;
|
||||
if (size > 0) {
|
||||
if (!memory->Allocate(size))
|
||||
return false;
|
||||
memory->Copy(reinterpret_cast<void*>(last_esp), size);
|
||||
loc->start_of_memory_range = 0 | last_esp;
|
||||
loc->memory = memory->location();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write CPU context based on signal context.
|
||||
bool WriteContext(MDRawContextX86 *context, const struct sigcontext *sig_ctx,
|
||||
const DebugRegs *debug_regs) {
|
||||
assert(sig_ctx != NULL);
|
||||
context->context_flags = MD_CONTEXT_X86_FULL;
|
||||
context->gs = sig_ctx->gs;
|
||||
context->fs = sig_ctx->fs;
|
||||
context->es = sig_ctx->es;
|
||||
context->ds = sig_ctx->ds;
|
||||
context->cs = sig_ctx->cs;
|
||||
context->ss = sig_ctx->ss;
|
||||
context->edi = sig_ctx->edi;
|
||||
context->esi = sig_ctx->esi;
|
||||
context->ebp = sig_ctx->ebp;
|
||||
context->esp = sig_ctx->esp;
|
||||
context->ebx = sig_ctx->ebx;
|
||||
context->edx = sig_ctx->edx;
|
||||
context->ecx = sig_ctx->ecx;
|
||||
context->eax = sig_ctx->eax;
|
||||
context->eip = sig_ctx->eip;
|
||||
context->eflags = sig_ctx->eflags;
|
||||
if (sig_ctx->fpstate != NULL) {
|
||||
context->context_flags = MD_CONTEXT_X86_FULL |
|
||||
MD_CONTEXT_X86_FLOATING_POINT;
|
||||
context->float_save.control_word = sig_ctx->fpstate->cw;
|
||||
context->float_save.status_word = sig_ctx->fpstate->sw;
|
||||
context->float_save.tag_word = sig_ctx->fpstate->tag;
|
||||
context->float_save.error_offset = sig_ctx->fpstate->ipoff;
|
||||
context->float_save.error_selector = sig_ctx->fpstate->cssel;
|
||||
context->float_save.data_offset = sig_ctx->fpstate->dataoff;
|
||||
context->float_save.data_selector = sig_ctx->fpstate->datasel;
|
||||
memcpy(context->float_save.register_area, sig_ctx->fpstate->_st,
|
||||
sizeof(context->float_save.register_area));
|
||||
}
|
||||
|
||||
if (debug_regs != NULL) {
|
||||
context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS;
|
||||
context->dr0 = debug_regs->dr0;
|
||||
context->dr1 = debug_regs->dr1;
|
||||
context->dr2 = debug_regs->dr2;
|
||||
context->dr3 = debug_regs->dr3;
|
||||
context->dr6 = debug_regs->dr6;
|
||||
context->dr7 = debug_regs->dr7;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write CPU context based on provided registers.
|
||||
bool WriteContext(MDRawContextX86 *context,
|
||||
const struct user_regs_struct *regs,
|
||||
const struct user_fpregs_struct *fp_regs,
|
||||
const DebugRegs *dbg_regs) {
|
||||
if (!context || !regs)
|
||||
return false;
|
||||
|
||||
context->context_flags = MD_CONTEXT_X86_FULL;
|
||||
|
||||
context->cs = regs->xcs;
|
||||
context->ds = regs->xds;
|
||||
context->es = regs->xes;
|
||||
context->fs = regs->xfs;
|
||||
context->gs = regs->xgs;
|
||||
context->ss = regs->xss;
|
||||
context->edi = regs->edi;
|
||||
context->esi = regs->esi;
|
||||
context->ebx = regs->ebx;
|
||||
context->edx = regs->edx;
|
||||
context->ecx = regs->ecx;
|
||||
context->eax = regs->eax;
|
||||
context->ebp = regs->ebp;
|
||||
context->eip = regs->eip;
|
||||
context->esp = regs->esp;
|
||||
context->eflags = regs->eflags;
|
||||
|
||||
if (dbg_regs != NULL) {
|
||||
context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS;
|
||||
context->dr0 = dbg_regs->dr0;
|
||||
context->dr1 = dbg_regs->dr1;
|
||||
context->dr2 = dbg_regs->dr2;
|
||||
context->dr3 = dbg_regs->dr3;
|
||||
context->dr6 = dbg_regs->dr6;
|
||||
context->dr7 = dbg_regs->dr7;
|
||||
}
|
||||
|
||||
if (fp_regs != NULL) {
|
||||
context->context_flags |= MD_CONTEXT_X86_FLOATING_POINT;
|
||||
context->float_save.control_word = fp_regs->cwd;
|
||||
context->float_save.status_word = fp_regs->swd;
|
||||
context->float_save.tag_word = fp_regs->twd;
|
||||
context->float_save.error_offset = fp_regs->fip;
|
||||
context->float_save.error_selector = fp_regs->fcs;
|
||||
context->float_save.data_offset = fp_regs->foo;
|
||||
context->float_save.data_selector = fp_regs->fos;
|
||||
context->float_save.data_selector = fp_regs->fos;
|
||||
|
||||
memcpy(context->float_save.register_area, fp_regs->st_space,
|
||||
sizeof(context->float_save.register_area));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write information about a crashed thread.
|
||||
// When a thread crash, kernel will write something on the stack for processing
|
||||
// signal. This makes the current stack not reliable, and our stack walker
|
||||
// won't figure out the whole call stack for this. So we write the stack at the
|
||||
// time of the crash into the minidump file, not the current stack.
|
||||
bool WriteCrashedThreadStream(MinidumpFileWriter *minidump_writer,
|
||||
const WriterArgument *writer_args,
|
||||
const ThreadInfo &thread_info,
|
||||
MDRawThread *thread) {
|
||||
assert(writer_args->sig_ctx != NULL);
|
||||
|
||||
thread->thread_id = thread_info.pid;
|
||||
|
||||
UntypedMDRVA memory(minidump_writer);
|
||||
if (!WriteThreadStack(writer_args->sig_ctx->ebp,
|
||||
writer_args->sig_ctx->esp,
|
||||
writer_args->thread_lister,
|
||||
&memory,
|
||||
&thread->stack))
|
||||
return false;
|
||||
|
||||
TypedMDRVA<MDRawContextX86> context(minidump_writer);
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
thread->thread_context = context.location();
|
||||
memset(context.get(), 0, sizeof(MDRawContextX86));
|
||||
return WriteContext(context.get(), writer_args->sig_ctx, NULL);
|
||||
}
|
||||
|
||||
// Write information about a thread.
|
||||
// This function only processes thread running normally at the crash.
|
||||
bool WriteThreadStream(MinidumpFileWriter *minidump_writer,
|
||||
const LinuxThread *thread_lister,
|
||||
const ThreadInfo &thread_info,
|
||||
MDRawThread *thread) {
|
||||
thread->thread_id = thread_info.pid;
|
||||
|
||||
struct user_regs_struct regs;
|
||||
memset(®s, 0, sizeof(regs));
|
||||
if (!thread_lister->GetRegisters(thread_info.pid, ®s)) {
|
||||
perror(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
UntypedMDRVA memory(minidump_writer);
|
||||
if (!WriteThreadStack(regs.ebp,
|
||||
regs.esp,
|
||||
thread_lister,
|
||||
&memory,
|
||||
&thread->stack))
|
||||
return false;
|
||||
|
||||
struct user_fpregs_struct fp_regs;
|
||||
DebugRegs dbg_regs;
|
||||
memset(&fp_regs, 0, sizeof(fp_regs));
|
||||
// Get all the registers.
|
||||
thread_lister->GetFPRegisters(thread_info.pid, &fp_regs);
|
||||
thread_lister->GetDebugRegisters(thread_info.pid, &dbg_regs);
|
||||
|
||||
// Write context
|
||||
TypedMDRVA<MDRawContextX86> context(minidump_writer);
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
thread->thread_context = context.location();
|
||||
memset(context.get(), 0, sizeof(MDRawContextX86));
|
||||
return WriteContext(context.get(), ®s, &fp_regs, &dbg_regs);
|
||||
}
|
||||
|
||||
bool WriteCPUInformation(MDRawSystemInfo *sys_info) {
|
||||
const char *proc_cpu_path = "/proc/cpuinfo";
|
||||
char line[128];
|
||||
char vendor_id[13];
|
||||
const char vendor_id_name[] = "vendor_id";
|
||||
const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
|
||||
|
||||
struct CpuInfoEntry {
|
||||
const char *info_name;
|
||||
int value;
|
||||
} cpu_info_table[] = {
|
||||
{ "processor", -1 },
|
||||
{ "model", 0 },
|
||||
{ "stepping", 0 },
|
||||
{ "cpuid level", 0 },
|
||||
{ NULL, -1 },
|
||||
};
|
||||
|
||||
memset(vendor_id, 0, sizeof(vendor_id));
|
||||
|
||||
FILE *fp = fopen(proc_cpu_path, "r");
|
||||
if (fp != NULL) {
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
CpuInfoEntry *entry = &cpu_info_table[0];
|
||||
while (entry->info_name != NULL) {
|
||||
if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
|
||||
char *value = strchr(line, ':');
|
||||
value++;
|
||||
if (value != NULL)
|
||||
sscanf(value, " %d", &(entry->value));
|
||||
}
|
||||
entry++;
|
||||
}
|
||||
|
||||
// special case for vendor_id
|
||||
if (!strncmp(line, vendor_id_name, vendor_id_name_length)) {
|
||||
char *value = strchr(line, ':');
|
||||
if (value == NULL)
|
||||
continue;
|
||||
|
||||
value++;
|
||||
while (*value && isspace(*value))
|
||||
value++;
|
||||
if (*value) {
|
||||
size_t length = strlen(value);
|
||||
// we don't want the trailing newline
|
||||
if (value[length - 1] == '\n')
|
||||
length--;
|
||||
// ensure we have space for the value
|
||||
if (length < sizeof(vendor_id))
|
||||
strncpy(vendor_id, value, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// /proc/cpuinfo contains cpu id, change it into number by adding one.
|
||||
cpu_info_table[0].value++;
|
||||
|
||||
sys_info->number_of_processors = cpu_info_table[0].value;
|
||||
sys_info->processor_level = cpu_info_table[3].value;
|
||||
sys_info->processor_revision = cpu_info_table[1].value << 8 |
|
||||
cpu_info_table[2].value;
|
||||
|
||||
sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
|
||||
struct utsname uts;
|
||||
if (uname(&uts) == 0) {
|
||||
// Match i*86 and x86* as X86 architecture.
|
||||
if ((strstr(uts.machine, "x86") == uts.machine) ||
|
||||
(strlen(uts.machine) == 4 &&
|
||||
uts.machine[0] == 'i' &&
|
||||
uts.machine[2] == '8' &&
|
||||
uts.machine[3] == '6')) {
|
||||
sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86;
|
||||
if (vendor_id[0] != '\0')
|
||||
memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
|
||||
sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteOSInformation(MinidumpFileWriter *minidump_writer,
|
||||
MDRawSystemInfo *sys_info) {
|
||||
sys_info->platform_id = MD_OS_LINUX;
|
||||
|
||||
struct utsname uts;
|
||||
if (uname(&uts) == 0) {
|
||||
char os_version[512];
|
||||
size_t space_left = sizeof(os_version);
|
||||
memset(os_version, 0, space_left);
|
||||
const char *os_info_table[] = {
|
||||
uts.sysname,
|
||||
uts.release,
|
||||
uts.version,
|
||||
uts.machine,
|
||||
"GNU/Linux",
|
||||
NULL
|
||||
};
|
||||
for (const char **cur_os_info = os_info_table;
|
||||
*cur_os_info != NULL;
|
||||
cur_os_info++) {
|
||||
if (cur_os_info != os_info_table && space_left > 1) {
|
||||
strcat(os_version, " ");
|
||||
space_left--;
|
||||
}
|
||||
if (space_left > strlen(*cur_os_info)) {
|
||||
strcat(os_version, *cur_os_info);
|
||||
space_left -= strlen(*cur_os_info);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MDLocationDescriptor location;
|
||||
if (!minidump_writer->WriteString(os_version, 0, &location))
|
||||
return false;
|
||||
sys_info->csd_version_rva = location.rva;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback context for get writting thread information.
|
||||
struct ThreadInfoCallbackCtx {
|
||||
MinidumpFileWriter *minidump_writer;
|
||||
const WriterArgument *writer_args;
|
||||
TypedMDRVA<MDRawThreadList> *list;
|
||||
int thread_index;
|
||||
};
|
||||
|
||||
// Callback run for writing threads information in the process.
|
||||
bool ThreadInfomationCallback(const ThreadInfo &thread_info,
|
||||
void *context) {
|
||||
ThreadInfoCallbackCtx *callback_context =
|
||||
static_cast<ThreadInfoCallbackCtx *>(context);
|
||||
bool success = true;
|
||||
MDRawThread thread;
|
||||
memset(&thread, 0, sizeof(MDRawThread));
|
||||
if (thread_info.pid != callback_context->writer_args->crashed_pid ||
|
||||
callback_context->writer_args->sig_ctx == NULL) {
|
||||
success = WriteThreadStream(callback_context->minidump_writer,
|
||||
callback_context->writer_args->thread_lister,
|
||||
thread_info, &thread);
|
||||
} else {
|
||||
success = WriteCrashedThreadStream(callback_context->minidump_writer,
|
||||
callback_context->writer_args,
|
||||
thread_info, &thread);
|
||||
}
|
||||
if (success) {
|
||||
callback_context->list->CopyIndexAfterObject(
|
||||
callback_context->thread_index++,
|
||||
&thread, sizeof(MDRawThread));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Stream writers
|
||||
bool WriteThreadListStream(MinidumpFileWriter *minidump_writer,
|
||||
const WriterArgument *writer_args,
|
||||
MDRawDirectory *dir) {
|
||||
// Get the thread information.
|
||||
const LinuxThread *thread_lister = writer_args->thread_lister;
|
||||
int thread_count = thread_lister->GetThreadCount();
|
||||
if (thread_count < 0)
|
||||
return false;
|
||||
TypedMDRVA<MDRawThreadList> list(minidump_writer);
|
||||
if (!list.AllocateObjectAndArray(thread_count, sizeof(MDRawThread)))
|
||||
return false;
|
||||
dir->stream_type = MD_THREAD_LIST_STREAM;
|
||||
dir->location = list.location();
|
||||
list.get()->number_of_threads = thread_count;
|
||||
|
||||
ThreadInfoCallbackCtx context;
|
||||
context.minidump_writer = minidump_writer;
|
||||
context.writer_args = writer_args;
|
||||
context.list = &list;
|
||||
context.thread_index = 0;
|
||||
CallbackParam<ThreadCallback> callback_param(ThreadInfomationCallback,
|
||||
&context);
|
||||
int written = thread_lister->ListThreads(&callback_param);
|
||||
return written == thread_count;
|
||||
}
|
||||
|
||||
bool WriteCVRecord(MinidumpFileWriter *minidump_writer,
|
||||
MDRawModule *module,
|
||||
const char *module_path) {
|
||||
TypedMDRVA<MDCVInfoPDB70> cv(minidump_writer);
|
||||
|
||||
// Only return the last path component of the full module path
|
||||
const char *module_name = strrchr(module_path, '/');
|
||||
// Increment past the slash
|
||||
if (module_name)
|
||||
++module_name;
|
||||
else
|
||||
module_name = "<Unknown>";
|
||||
|
||||
size_t module_name_length = strlen(module_name);
|
||||
if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
|
||||
return false;
|
||||
if (!cv.CopyIndexAfterObject(0, const_cast<char *>(module_name),
|
||||
module_name_length))
|
||||
return false;
|
||||
|
||||
module->cv_record = cv.location();
|
||||
MDCVInfoPDB70 *cv_ptr = cv.get();
|
||||
memset(cv_ptr, 0, sizeof(MDCVInfoPDB70));
|
||||
cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
|
||||
cv_ptr->age = 0;
|
||||
|
||||
// Get the module identifier
|
||||
FileID file_id(module_path);
|
||||
unsigned char identifier[16];
|
||||
|
||||
if (file_id.ElfFileIdentifier(identifier)) {
|
||||
cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
|
||||
(uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
|
||||
(uint32_t)identifier[3];
|
||||
cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
|
||||
cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
|
||||
cv_ptr->signature.data4[0] = identifier[8];
|
||||
cv_ptr->signature.data4[1] = identifier[9];
|
||||
cv_ptr->signature.data4[2] = identifier[10];
|
||||
cv_ptr->signature.data4[3] = identifier[11];
|
||||
cv_ptr->signature.data4[4] = identifier[12];
|
||||
cv_ptr->signature.data4[5] = identifier[13];
|
||||
cv_ptr->signature.data4[6] = identifier[14];
|
||||
cv_ptr->signature.data4[7] = identifier[15];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ModuleInfoCallbackCtx {
|
||||
MinidumpFileWriter *minidump_writer;
|
||||
const WriterArgument *writer_args;
|
||||
TypedMDRVA<MDRawModuleList> *list;
|
||||
int module_index;
|
||||
};
|
||||
|
||||
bool ModuleInfoCallback(const ModuleInfo &module_info,
|
||||
void *context) {
|
||||
ModuleInfoCallbackCtx *callback_context =
|
||||
static_cast<ModuleInfoCallbackCtx *>(context);
|
||||
// Skip those modules without name, or those that are not modules.
|
||||
if (strlen(module_info.name) == 0 ||
|
||||
!strchr(module_info.name, '/'))
|
||||
return true;
|
||||
|
||||
MDRawModule module;
|
||||
memset(&module, 0, sizeof(module));
|
||||
MDLocationDescriptor loc;
|
||||
if (!callback_context->minidump_writer->WriteString(module_info.name, 0,
|
||||
&loc))
|
||||
return false;
|
||||
module.base_of_image = (u_int64_t)module_info.start_addr;
|
||||
module.size_of_image = module_info.size;
|
||||
module.module_name_rva = loc.rva;
|
||||
|
||||
if (!WriteCVRecord(callback_context->minidump_writer, &module,
|
||||
module_info.name))
|
||||
return false;
|
||||
callback_context->list->CopyIndexAfterObject(
|
||||
callback_context->module_index++, &module, MD_MODULE_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteModuleListStream(MinidumpFileWriter *minidump_writer,
|
||||
const WriterArgument *writer_args,
|
||||
MDRawDirectory *dir) {
|
||||
TypedMDRVA<MDRawModuleList> list(minidump_writer);
|
||||
int module_count = writer_args->thread_lister->GetModuleCount();
|
||||
if (module_count <= 0 ||
|
||||
!list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE))
|
||||
return false;
|
||||
dir->stream_type = MD_MODULE_LIST_STREAM;
|
||||
dir->location = list.location();
|
||||
list.get()->number_of_modules = module_count;
|
||||
ModuleInfoCallbackCtx context;
|
||||
context.minidump_writer = minidump_writer;
|
||||
context.writer_args = writer_args;
|
||||
context.list = &list;
|
||||
context.module_index = 0;
|
||||
CallbackParam<ModuleCallback> callback(ModuleInfoCallback, &context);
|
||||
return writer_args->thread_lister->ListModules(&callback) == module_count;
|
||||
}
|
||||
|
||||
bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer,
|
||||
const WriterArgument *writer_args,
|
||||
MDRawDirectory *dir) {
|
||||
TypedMDRVA<MDRawSystemInfo> sys_info(minidump_writer);
|
||||
if (!sys_info.Allocate())
|
||||
return false;
|
||||
dir->stream_type = MD_SYSTEM_INFO_STREAM;
|
||||
dir->location = sys_info.location();
|
||||
|
||||
return WriteCPUInformation(sys_info.get()) &&
|
||||
WriteOSInformation(minidump_writer, sys_info.get());
|
||||
}
|
||||
|
||||
bool WriteExceptionStream(MinidumpFileWriter *minidump_writer,
|
||||
const WriterArgument *writer_args,
|
||||
MDRawDirectory *dir) {
|
||||
// This happenes when this is not a crash, but a requested dump.
|
||||
if (writer_args->sig_ctx == NULL)
|
||||
return false;
|
||||
|
||||
TypedMDRVA<MDRawExceptionStream> exception(minidump_writer);
|
||||
if (!exception.Allocate())
|
||||
return false;
|
||||
|
||||
dir->stream_type = MD_EXCEPTION_STREAM;
|
||||
dir->location = exception.location();
|
||||
exception.get()->thread_id = writer_args->crashed_pid;
|
||||
exception.get()->exception_record.exception_code = writer_args->signo;
|
||||
exception.get()->exception_record.exception_flags = 0;
|
||||
if (writer_args->sig_ctx != NULL) {
|
||||
exception.get()->exception_record.exception_address =
|
||||
writer_args->sig_ctx->eip;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write context of the exception.
|
||||
TypedMDRVA<MDRawContextX86> context(minidump_writer);
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
exception.get()->thread_context = context.location();
|
||||
memset(context.get(), 0, sizeof(MDRawContextX86));
|
||||
return WriteContext(context.get(), writer_args->sig_ctx, NULL);
|
||||
}
|
||||
|
||||
bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer,
|
||||
const WriterArgument *writer_args,
|
||||
MDRawDirectory *dir) {
|
||||
TypedMDRVA<MDRawMiscInfo> info(minidump_writer);
|
||||
if (!info.Allocate())
|
||||
return false;
|
||||
|
||||
dir->stream_type = MD_MISC_INFO_STREAM;
|
||||
dir->location = info.location();
|
||||
info.get()->size_of_info = sizeof(MDRawMiscInfo);
|
||||
info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID;
|
||||
info.get()->process_id = writer_args->requester_pid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer,
|
||||
const WriterArgument *writer_args,
|
||||
MDRawDirectory *dir) {
|
||||
TypedMDRVA<MDRawBreakpadInfo> info(minidump_writer);
|
||||
if (!info.Allocate())
|
||||
return false;
|
||||
|
||||
dir->stream_type = MD_BREAKPAD_INFO_STREAM;
|
||||
dir->location = info.location();
|
||||
|
||||
info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
|
||||
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
|
||||
info.get()->dump_thread_id = getpid();
|
||||
info.get()->requesting_thread_id = writer_args->requester_pid;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prototype of writer functions.
|
||||
typedef bool (*WriteStringFN)(MinidumpFileWriter *,
|
||||
const WriterArgument *,
|
||||
MDRawDirectory *);
|
||||
|
||||
// Function table to writer a full minidump.
|
||||
WriteStringFN writers[] = {
|
||||
WriteThreadListStream,
|
||||
WriteModuleListStream,
|
||||
WriteSystemInfoStream,
|
||||
WriteExceptionStream,
|
||||
WriteMiscInfoStream,
|
||||
WriteBreakpadInfoStream,
|
||||
};
|
||||
|
||||
// Will call each writer function in the writers table.
|
||||
// It runs in a different process from the crashing process, but sharing
|
||||
// the same address space. This enables it to use ptrace functions.
|
||||
int Write(void *argument) {
|
||||
WriterArgument *writer_args =
|
||||
static_cast<WriterArgument *>(argument);
|
||||
|
||||
if (!writer_args->thread_lister->SuspendAllThreads())
|
||||
return -1;
|
||||
|
||||
if (writer_args->sighandler_ebp != 0 &&
|
||||
writer_args->thread_lister->FindSigContext(writer_args->sighandler_ebp,
|
||||
&writer_args->sig_ctx)) {
|
||||
writer_args->crashed_stack_bottom =
|
||||
writer_args->thread_lister->GetThreadStackBottom(
|
||||
writer_args->sig_ctx->ebp);
|
||||
int crashed_pid = FindCrashingThread(writer_args->crashed_stack_bottom,
|
||||
writer_args->requester_pid,
|
||||
writer_args->thread_lister);
|
||||
if (crashed_pid > 0)
|
||||
writer_args->crashed_pid = crashed_pid;
|
||||
}
|
||||
|
||||
|
||||
MinidumpFileWriter *minidump_writer = writer_args->minidump_writer;
|
||||
TypedMDRVA<MDRawHeader> header(minidump_writer);
|
||||
TypedMDRVA<MDRawDirectory> dir(minidump_writer);
|
||||
if (!header.Allocate())
|
||||
return 0;
|
||||
|
||||
int writer_count = sizeof(writers) / sizeof(writers[0]);
|
||||
// Need directory space for all writers.
|
||||
if (!dir.AllocateArray(writer_count))
|
||||
return 0;
|
||||
header.get()->signature = MD_HEADER_SIGNATURE;
|
||||
header.get()->version = MD_HEADER_VERSION;
|
||||
header.get()->time_date_stamp = time(NULL);
|
||||
header.get()->stream_count = writer_count;
|
||||
header.get()->stream_directory_rva = dir.position();
|
||||
|
||||
int dir_index = 0;
|
||||
MDRawDirectory local_dir;
|
||||
for (int i = 0; i < writer_count; ++i) {
|
||||
if (writers[i](minidump_writer, writer_args, &local_dir))
|
||||
dir.CopyIndex(dir_index++, &local_dir);
|
||||
}
|
||||
|
||||
writer_args->thread_lister->ResumeAllThreads();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
MinidumpGenerator::MinidumpGenerator() {
|
||||
AllocateStack();
|
||||
}
|
||||
|
||||
MinidumpGenerator::~MinidumpGenerator() {
|
||||
}
|
||||
|
||||
void MinidumpGenerator::AllocateStack() {
|
||||
stack_.reset(new char[kStackSize]);
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname,
|
||||
int signo,
|
||||
uintptr_t sighandler_ebp,
|
||||
struct sigcontext **sig_ctx) const {
|
||||
assert(file_pathname != NULL);
|
||||
assert(stack_ != NULL);
|
||||
|
||||
if (stack_ == NULL || file_pathname == NULL)
|
||||
return false;
|
||||
|
||||
MinidumpFileWriter minidump_writer;
|
||||
if (minidump_writer.Open(file_pathname)) {
|
||||
WriterArgument argument;
|
||||
memset(&argument, 0, sizeof(argument));
|
||||
LinuxThread thread_lister(getpid());
|
||||
argument.thread_lister = &thread_lister;
|
||||
argument.minidump_writer = &minidump_writer;
|
||||
argument.requester_pid = getpid();
|
||||
argument.crashed_pid = getpid();
|
||||
argument.signo = signo;
|
||||
argument.sighandler_ebp = sighandler_ebp;
|
||||
argument.sig_ctx = NULL;
|
||||
|
||||
int cloned_pid = clone(Write, stack_.get() + kStackSize,
|
||||
CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
(void*)&argument);
|
||||
waitpid(cloned_pid, NULL, __WALL);
|
||||
if (sig_ctx != NULL)
|
||||
*sig_ctx = argument.sig_ctx;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
84
src/client/linux/log/log.cc
Normal file
84
src/client/linux/log/log.cc
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2012 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/log/log.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
#include <dlfcn.h>
|
||||
#else
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#endif
|
||||
|
||||
namespace logger {
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
namespace {
|
||||
|
||||
// __android_log_buf_write() is not exported in the NDK and is being used by
|
||||
// dynamic runtime linking. Its declaration is taken from Android's
|
||||
// system/core/include/log/log.h.
|
||||
using AndroidLogBufferWriteFunc = int (*)(int bufID, int prio, const char *tag,
|
||||
const char *text);
|
||||
const int kAndroidCrashLogId = 4; // From LOG_ID_CRASH in log.h.
|
||||
const char kAndroidLogTag[] = "google-breakpad";
|
||||
|
||||
bool g_crash_log_initialized = false;
|
||||
AndroidLogBufferWriteFunc g_android_log_buf_write = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
void initializeCrashLogWriter() {
|
||||
if (g_crash_log_initialized)
|
||||
return;
|
||||
g_android_log_buf_write = reinterpret_cast<AndroidLogBufferWriteFunc>(
|
||||
dlsym(RTLD_DEFAULT, "__android_log_buf_write"));
|
||||
g_crash_log_initialized = true;
|
||||
}
|
||||
|
||||
int writeToCrashLog(const char* buf) {
|
||||
// Try writing to the crash log ring buffer. If not available, fall back to
|
||||
// the standard log buffer.
|
||||
if (g_android_log_buf_write) {
|
||||
return g_android_log_buf_write(kAndroidCrashLogId, ANDROID_LOG_FATAL,
|
||||
kAndroidLogTag, buf);
|
||||
}
|
||||
return __android_log_write(ANDROID_LOG_FATAL, kAndroidLogTag, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
int write(const char* buf, size_t nbytes) {
|
||||
#if defined(__ANDROID__)
|
||||
return __android_log_write(ANDROID_LOG_WARN, kAndroidLogTag, buf);
|
||||
#else
|
||||
return sys_write(2, buf, nbytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace logger
|
55
src/client/linux/log/log.h
Normal file
55
src/client/linux/log/log.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_LOG_LOG_H_
|
||||
#define CLIENT_LINUX_LOG_LOG_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace logger {
|
||||
|
||||
int write(const char* buf, size_t nbytes);
|
||||
|
||||
// In the case of Android the log can be written to the default system log
|
||||
// (default behavior of write() above, or to the crash log (see
|
||||
// writeToCrashLog() below).
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
// The logger must be initialized in a non-compromised context.
|
||||
void initializeCrashLogWriter();
|
||||
|
||||
// Once initialized, writeToCrashLog is safe to use in a compromised context,
|
||||
// even if the initialization failed, in which case this will silently fall
|
||||
// back on write().
|
||||
int writeToCrashLog(const char* buf);
|
||||
#endif
|
||||
|
||||
} // namespace logger
|
||||
|
||||
#endif // CLIENT_LINUX_LOG_LOG_H_
|
598
src/client/linux/microdump_writer/microdump_writer.cc
Normal file
598
src/client/linux/microdump_writer/microdump_writer.cc
Normal file
@ -0,0 +1,598 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// This translation unit generates microdumps into the console (logcat on
|
||||
// Android). See crbug.com/410294 for more info and design docs.
|
||||
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "client/linux/log/log.h"
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/memory.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::auto_wasteful_vector;
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using google_breakpad::kDefaultBuildIdSize;
|
||||
using google_breakpad::LinuxDumper;
|
||||
using google_breakpad::LinuxPtraceDumper;
|
||||
using google_breakpad::MappingInfo;
|
||||
using google_breakpad::MappingList;
|
||||
using google_breakpad::MicrodumpExtraInfo;
|
||||
using google_breakpad::RawContextCPU;
|
||||
using google_breakpad::ThreadInfo;
|
||||
using google_breakpad::UContextReader;
|
||||
|
||||
const size_t kLineBufferSize = 2048;
|
||||
|
||||
#if !defined(__LP64__)
|
||||
// The following are only used by DumpFreeSpace, so need to be compiled
|
||||
// in conditionally in the same way.
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
Dst saturated_cast(Src src) {
|
||||
if (src >= std::numeric_limits<Dst>::max())
|
||||
return std::numeric_limits<Dst>::max();
|
||||
if (src <= std::numeric_limits<Dst>::min())
|
||||
return std::numeric_limits<Dst>::min();
|
||||
return static_cast<Dst>(src);
|
||||
}
|
||||
|
||||
int Log2Floor(uint64_t n) {
|
||||
// Copied from chromium src/base/bits.h
|
||||
if (n == 0)
|
||||
return -1;
|
||||
int log = 0;
|
||||
uint64_t value = n;
|
||||
for (int i = 5; i >= 0; --i) {
|
||||
int shift = (1 << i);
|
||||
uint64_t x = value >> shift;
|
||||
if (x != 0) {
|
||||
value = x;
|
||||
log += shift;
|
||||
}
|
||||
}
|
||||
assert(value == 1u);
|
||||
return log;
|
||||
}
|
||||
|
||||
bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) {
|
||||
// Because of load biasing, we can end up with a situation where two
|
||||
// mappings actually overlap. So we will define adjacency to also include a
|
||||
// b start address that lies within a's address range (including starting
|
||||
// immediately after a).
|
||||
// Because load biasing only ever moves the start address backwards, the end
|
||||
// address should still increase.
|
||||
return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr;
|
||||
}
|
||||
|
||||
bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) {
|
||||
// Return true if mapping a is before mapping b.
|
||||
// For the same reason (load biasing) we compare end addresses, which - unlike
|
||||
// start addresses - will not have been modified.
|
||||
return a->start_addr + a->size < b->start_addr + b->size;
|
||||
}
|
||||
|
||||
size_t NextOrderedMapping(
|
||||
const google_breakpad::wasteful_vector<MappingInfo*>& mappings,
|
||||
size_t curr) {
|
||||
// Find the mapping that directly follows mappings[curr].
|
||||
// If no such mapping exists, return |invalid| to indicate this.
|
||||
const size_t invalid = std::numeric_limits<size_t>::max();
|
||||
size_t best = invalid;
|
||||
for (size_t next = 0; next < mappings.size(); ++next) {
|
||||
if (MappingLessThan(mappings[curr], mappings[next]) &&
|
||||
(best == invalid || MappingLessThan(mappings[next], mappings[best]))) {
|
||||
best = next;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
#endif // !__LP64__
|
||||
|
||||
class MicrodumpWriter {
|
||||
public:
|
||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
LinuxDumper* dumper)
|
||||
: ucontext_(context ? &context->context : NULL),
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
float_state_(context ? &context->float_state : NULL),
|
||||
#endif
|
||||
dumper_(dumper),
|
||||
mapping_list_(mappings),
|
||||
microdump_extra_info_(microdump_extra_info),
|
||||
log_line_(NULL) {
|
||||
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
|
||||
if (log_line_)
|
||||
log_line_[0] = '\0'; // Clear out the log line buffer.
|
||||
}
|
||||
|
||||
~MicrodumpWriter() { dumper_->ThreadsResume(); }
|
||||
|
||||
bool Init() {
|
||||
// In the exceptional case where the system was out of memory and there
|
||||
// wasn't even room to allocate the line buffer, bail out. There is nothing
|
||||
// useful we can possibly achieve without the ability to Log. At least let's
|
||||
// try to not crash.
|
||||
if (!dumper_->Init() || !log_line_)
|
||||
return false;
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
bool success;
|
||||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||
DumpProductInformation();
|
||||
DumpOSInformation();
|
||||
DumpGPUInformation();
|
||||
#if !defined(__LP64__)
|
||||
DumpFreeSpace();
|
||||
#endif
|
||||
success = DumpCrashingThread();
|
||||
if (success)
|
||||
success = DumpMappings();
|
||||
LogLine("-----END BREAKPAD MICRODUMP-----");
|
||||
dumper_->ThreadsResume();
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
// Writes one line to the system log.
|
||||
void LogLine(const char* msg) {
|
||||
#if defined(__ANDROID__)
|
||||
logger::writeToCrashLog(msg);
|
||||
#else
|
||||
logger::write(msg, my_strlen(msg));
|
||||
logger::write("\n", 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Stages the given string in the current line buffer.
|
||||
void LogAppend(const char* str) {
|
||||
my_strlcat(log_line_, str, kLineBufferSize);
|
||||
}
|
||||
|
||||
// As above (required to take precedence over template specialization below).
|
||||
void LogAppend(char* str) {
|
||||
LogAppend(const_cast<const char*>(str));
|
||||
}
|
||||
|
||||
// Stages the hex repr. of the given int type in the current line buffer.
|
||||
template<typename T>
|
||||
void LogAppend(T value) {
|
||||
// Make enough room to hex encode the largest int type + NUL.
|
||||
static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
char hexstr[sizeof(T) * 2 + 1];
|
||||
for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4)
|
||||
hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F];
|
||||
hexstr[sizeof(T) * 2] = '\0';
|
||||
LogAppend(hexstr);
|
||||
}
|
||||
|
||||
// Stages the buffer content hex-encoded in the current line buffer.
|
||||
void LogAppend(const void* buf, size_t length) {
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf);
|
||||
for (size_t i = 0; i < length; ++i, ++ptr)
|
||||
LogAppend(*ptr);
|
||||
}
|
||||
|
||||
// Writes out the current line buffer on the system log.
|
||||
void LogCommitLine() {
|
||||
LogLine(log_line_);
|
||||
my_strlcpy(log_line_, "", kLineBufferSize);
|
||||
}
|
||||
|
||||
void DumpProductInformation() {
|
||||
LogAppend("V ");
|
||||
if (microdump_extra_info_.product_info) {
|
||||
LogAppend(microdump_extra_info_.product_info);
|
||||
} else {
|
||||
LogAppend("UNKNOWN:0.0.0.0");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpOSInformation() {
|
||||
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
const char kOSId[] = "A";
|
||||
#else
|
||||
const char kOSId[] = "L";
|
||||
#endif
|
||||
|
||||
// Dump the runtime architecture. On multiarch devices it might not match the
|
||||
// hw architecture (the one returned by uname()), for instance in the case of
|
||||
// a 32-bit app running on a aarch64 device.
|
||||
#if defined(__aarch64__)
|
||||
const char kArch[] = "arm64";
|
||||
#elif defined(__ARMEL__)
|
||||
const char kArch[] = "arm";
|
||||
#elif defined(__x86_64__)
|
||||
const char kArch[] = "x86_64";
|
||||
#elif defined(__i386__)
|
||||
const char kArch[] = "x86";
|
||||
#elif defined(__mips__)
|
||||
# if _MIPS_SIM == _ABIO32
|
||||
const char kArch[] = "mips";
|
||||
# elif _MIPS_SIM == _ABI64
|
||||
const char kArch[] = "mips64";
|
||||
# else
|
||||
# error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet"
|
||||
#endif
|
||||
|
||||
LogAppend("O ");
|
||||
LogAppend(kOSId);
|
||||
LogAppend(" ");
|
||||
LogAppend(kArch);
|
||||
LogAppend(" ");
|
||||
LogAppend(n_cpus);
|
||||
LogAppend(" ");
|
||||
|
||||
// Dump the HW architecture (e.g., armv7l, aarch64).
|
||||
struct utsname uts;
|
||||
const bool has_uts_info = (uname(&uts) == 0);
|
||||
const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch";
|
||||
LogAppend(hwArch);
|
||||
LogAppend(" ");
|
||||
|
||||
// If the client has attached a build fingerprint to the MinidumpDescriptor
|
||||
// use that one. Otherwise try to get some basic info from uname().
|
||||
if (microdump_extra_info_.build_fingerprint) {
|
||||
LogAppend(microdump_extra_info_.build_fingerprint);
|
||||
} else if (has_uts_info) {
|
||||
LogAppend(uts.release);
|
||||
LogAppend(" ");
|
||||
LogAppend(uts.version);
|
||||
} else {
|
||||
LogAppend("no build fingerprint available");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpGPUInformation() {
|
||||
LogAppend("G ");
|
||||
if (microdump_extra_info_.gpu_fingerprint) {
|
||||
LogAppend(microdump_extra_info_.gpu_fingerprint);
|
||||
} else {
|
||||
LogAppend("UNKNOWN");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
bool DumpThreadStack(uint32_t thread_id,
|
||||
uintptr_t stack_pointer,
|
||||
int max_stack_len,
|
||||
uint8_t** stack_copy) {
|
||||
*stack_copy = NULL;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||
// The stack pointer might not be available. In this case we don't hard
|
||||
// fail, just produce a (almost useless) microdump w/o a stack section.
|
||||
return true;
|
||||
}
|
||||
|
||||
LogAppend("S 0 ");
|
||||
LogAppend(stack_pointer);
|
||||
LogAppend(" ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack));
|
||||
LogAppend(" ");
|
||||
LogAppend(stack_len);
|
||||
LogCommitLine();
|
||||
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||
stack_len = max_stack_len;
|
||||
}
|
||||
|
||||
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
|
||||
|
||||
// Dump the content of the stack, splicing it into chunks which size is
|
||||
// compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
|
||||
const size_t STACK_DUMP_CHUNK_SIZE = 384;
|
||||
for (size_t stack_off = 0; stack_off < stack_len;
|
||||
stack_off += STACK_DUMP_CHUNK_SIZE) {
|
||||
LogAppend("S ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
|
||||
LogAppend(" ");
|
||||
LogAppend(*stack_copy + stack_off,
|
||||
std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
|
||||
LogCommitLine();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write information about the crashing thread.
|
||||
bool DumpCrashingThread() {
|
||||
const unsigned num_threads = dumper_->threads().size();
|
||||
|
||||
for (unsigned i = 0; i < num_threads; ++i) {
|
||||
MDRawThread thread;
|
||||
my_memset(&thread, 0, sizeof(thread));
|
||||
thread.thread_id = dumper_->threads()[i];
|
||||
|
||||
// Dump only the crashing thread.
|
||||
if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
|
||||
continue;
|
||||
|
||||
assert(ucontext_);
|
||||
assert(!dumper_->IsPostMortem());
|
||||
|
||||
uint8_t* stack_copy;
|
||||
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||
if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
|
||||
return false;
|
||||
|
||||
RawContextCPU cpu;
|
||||
my_memset(&cpu, 0, sizeof(RawContextCPU));
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
|
||||
#else
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||
#endif
|
||||
DumpCPUState(&cpu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpCPUState(RawContextCPU* cpu) {
|
||||
LogAppend("C ");
|
||||
LogAppend(cpu, sizeof(*cpu));
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
// If there is caller-provided information about this mapping
|
||||
// in the mapping_list_ list, return true. Otherwise, return false.
|
||||
bool HaveMappingInfo(const MappingInfo& mapping) {
|
||||
for (MappingList::const_iterator iter = mapping_list_.begin();
|
||||
iter != mapping_list_.end();
|
||||
++iter) {
|
||||
// Ignore any mappings that are wholly contained within
|
||||
// mappings in the mapping_info_ list.
|
||||
if (mapping.start_addr >= iter->first.start_addr &&
|
||||
(mapping.start_addr + mapping.size) <=
|
||||
(iter->first.start_addr + iter->first.size)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dump information about the provided |mapping|. If |identifier| is non-NULL,
|
||||
// use it instead of calculating a file ID from the mapping.
|
||||
void DumpModule(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
const uint8_t* identifier) {
|
||||
|
||||
auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
|
||||
dumper_->allocator());
|
||||
|
||||
if (identifier) {
|
||||
// GUID was provided by caller.
|
||||
identifier_bytes.insert(identifier_bytes.end(),
|
||||
identifier,
|
||||
identifier + sizeof(MDGUID));
|
||||
} else {
|
||||
dumper_->ElfFileIdentifierForMapping(
|
||||
mapping,
|
||||
member,
|
||||
mapping_id,
|
||||
identifier_bytes);
|
||||
}
|
||||
|
||||
// Copy as many bytes of |identifier| as will fit into a MDGUID
|
||||
MDGUID module_identifier = {0};
|
||||
memcpy(&module_identifier, &identifier_bytes[0],
|
||||
std::min(sizeof(MDGUID), identifier_bytes.size()));
|
||||
|
||||
char file_name[NAME_MAX];
|
||||
char file_path[NAME_MAX];
|
||||
dumper_->GetMappingEffectiveNameAndPath(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||
|
||||
LogAppend("M ");
|
||||
LogAppend(static_cast<uintptr_t>(mapping.start_addr));
|
||||
LogAppend(" ");
|
||||
LogAppend(mapping.offset);
|
||||
LogAppend(" ");
|
||||
LogAppend(mapping.size);
|
||||
LogAppend(" ");
|
||||
LogAppend(module_identifier.data1);
|
||||
LogAppend(module_identifier.data2);
|
||||
LogAppend(module_identifier.data3);
|
||||
LogAppend(module_identifier.data4[0]);
|
||||
LogAppend(module_identifier.data4[1]);
|
||||
LogAppend(module_identifier.data4[2]);
|
||||
LogAppend(module_identifier.data4[3]);
|
||||
LogAppend(module_identifier.data4[4]);
|
||||
LogAppend(module_identifier.data4[5]);
|
||||
LogAppend(module_identifier.data4[6]);
|
||||
LogAppend(module_identifier.data4[7]);
|
||||
LogAppend("0 "); // Age is always 0 on Linux.
|
||||
LogAppend(file_name);
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
#if !defined(__LP64__)
|
||||
void DumpFreeSpace() {
|
||||
const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
|
||||
dumper_->mappings();
|
||||
if (mappings.size() == 0) return;
|
||||
|
||||
// This is complicated by the fact that mappings is not in order. It should
|
||||
// be mostly in order, however the mapping that contains the entry point for
|
||||
// the process is always at the front of the vector.
|
||||
|
||||
static const int HBITS = sizeof(size_t) * 8;
|
||||
size_t hole_histogram[HBITS];
|
||||
my_memset(hole_histogram, 0, sizeof(hole_histogram));
|
||||
|
||||
// Find the lowest address mapping.
|
||||
size_t curr = 0;
|
||||
for (size_t i = 1; i < mappings.size(); ++i) {
|
||||
if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i;
|
||||
}
|
||||
|
||||
uintptr_t lo_addr = mappings[curr]->start_addr;
|
||||
|
||||
size_t hole_cnt = 0;
|
||||
size_t hole_max = 0;
|
||||
size_t hole_sum = 0;
|
||||
|
||||
while (true) {
|
||||
// Skip to the end of an adjacent run of mappings. This is an optimization
|
||||
// for the fact that mappings is mostly sorted.
|
||||
while (curr != mappings.size() - 1 &&
|
||||
MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) {
|
||||
++curr;
|
||||
}
|
||||
|
||||
size_t next = NextOrderedMapping(mappings, curr);
|
||||
if (next == std::numeric_limits<size_t>::max())
|
||||
break;
|
||||
|
||||
uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size;
|
||||
uintptr_t hole_hi = mappings[next]->start_addr;
|
||||
|
||||
if (hole_hi > hole_lo) {
|
||||
size_t hole_sz = hole_hi - hole_lo;
|
||||
hole_sum += hole_sz;
|
||||
hole_max = std::max(hole_sz, hole_max);
|
||||
++hole_cnt;
|
||||
++hole_histogram[Log2Floor(hole_sz)];
|
||||
}
|
||||
curr = next;
|
||||
}
|
||||
|
||||
uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size;
|
||||
|
||||
LogAppend("H ");
|
||||
LogAppend(lo_addr);
|
||||
LogAppend(" ");
|
||||
LogAppend(hi_addr);
|
||||
LogAppend(" ");
|
||||
LogAppend(saturated_cast<uint16_t>(hole_cnt));
|
||||
LogAppend(" ");
|
||||
LogAppend(hole_max);
|
||||
LogAppend(" ");
|
||||
LogAppend(hole_sum);
|
||||
for (unsigned int i = 0; i < HBITS; ++i) {
|
||||
if (!hole_histogram[i]) continue;
|
||||
LogAppend(" ");
|
||||
LogAppend(saturated_cast<uint8_t>(i));
|
||||
LogAppend(":");
|
||||
LogAppend(saturated_cast<uint8_t>(hole_histogram[i]));
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write information about the mappings in effect.
|
||||
bool DumpMappings() {
|
||||
// First write all the mappings from the dumper
|
||||
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
|
||||
const MappingInfo& mapping = *dumper_->mappings()[i];
|
||||
if (mapping.name[0] == 0 || // only want modules with filenames.
|
||||
!mapping.exec || // only want executable mappings.
|
||||
mapping.size < 4096 || // too small to get a signature for.
|
||||
HaveMappingInfo(mapping)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DumpModule(mapping, true, i, NULL);
|
||||
}
|
||||
// Next write all the mappings provided by the caller
|
||||
for (MappingList::const_iterator iter = mapping_list_.begin();
|
||||
iter != mapping_list_.end();
|
||||
++iter) {
|
||||
DumpModule(iter->first, false, 0, iter->second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
|
||||
|
||||
const struct ucontext* const ucontext_;
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
const google_breakpad::fpstate_t* const float_state_;
|
||||
#endif
|
||||
LinuxDumper* dumper_;
|
||||
const MappingList& mapping_list_;
|
||||
const MicrodumpExtraInfo microdump_extra_info_;
|
||||
char* log_line_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info) {
|
||||
LinuxPtraceDumper dumper(crashing_process);
|
||||
const ExceptionHandler::CrashContext* context = NULL;
|
||||
if (blob) {
|
||||
if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
||||
return false;
|
||||
context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
||||
dumper.set_crash_address(
|
||||
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
|
||||
dumper.set_crash_signal(context->siginfo.si_signo);
|
||||
dumper.set_crash_thread(context->tid);
|
||||
}
|
||||
MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
65
src/client/linux/microdump_writer/microdump_writer.h
Normal file
65
src/client/linux/microdump_writer/microdump_writer.h
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/mapping_info.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct MicrodumpExtraInfo;
|
||||
|
||||
// Writes a microdump (a reduced dump containing only the state of the crashing
|
||||
// thread) on the console (logcat on Android). These functions do not malloc nor
|
||||
// use libc functions which may. Thus, it can be used in contexts where the
|
||||
// state of the heap may be corrupt.
|
||||
// Args:
|
||||
// crashing_process: the pid of the crashing process. This must be trusted.
|
||||
// blob: a blob of data from the crashing process. See exception_handler.h
|
||||
// blob_size: the length of |blob| in bytes.
|
||||
// mappings: a list of additional mappings provided by the application.
|
||||
// build_fingerprint: a (optional) C string which determines the OS
|
||||
// build fingerprint (e.g., aosp/occam/mako:5.1.1/LMY47W/1234:eng/dev-keys).
|
||||
// product_info: a (optional) C string which determines the product name and
|
||||
// version (e.g., WebView:42.0.2311.136).
|
||||
//
|
||||
// Returns true iff successful.
|
||||
bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user