mirror of https://github.com/NekoX-Dev/NekoX.git
Compare commits
620 Commits
v7.9.3-pre
...
main
Author | SHA1 | Date |
---|---|---|
luvletter2333 | e72c1741bf | |
luvletter2333 | 6a5b90ed4b | |
luvletter2333 | f469c7fef9 | |
luvletter2333 | eb8eb1474d | |
luvletter2333 | c0fdbb58c0 | |
luvletter2333 | e63f66f505 | |
luvletter2333 | 71b3bb5e01 | |
luvletter2333 | 13d2187bdb | |
luvletter2333 | 92e5043254 | |
luvletter2333 | 4799b030b4 | |
Luv Letter | e392d0db9b | |
luvletter2333 | 4933c7107b | |
xaxtix | bab9943ee0 | |
xaxtix | 5e5b7659cb | |
xaxtix | c06f56dcad | |
luvletter2333 | 6d0964f995 | |
luvletter2333 | cf7001b2fc | |
luvletter2333 | 733feb011e | |
luvletter2333 | 1e59e4e335 | |
luvletter2333 | da6d0bea22 | |
luvletter2333 | 1063f56a25 | |
luvletter2333 | c068a74dd8 | |
xaxtix | 07a2c9a330 | |
xaxtix | 1dcd153fe2 | |
xaxtix | 1a48adbec4 | |
xaxtix | d374d4875d | |
luvletter2333 | 021af48ee3 | |
luvletter2333 | ed4fdf2fe3 | |
luvletter2333 | 14e962d30a | |
luvletter2333 | cedce6813e | |
luvletter2333 | afe1f215c8 | |
luvletter2333 | 0ee46c7b1c | |
luvletter2333 | 1497c7ddd3 | |
luvletter2333 | d0bf3ef05b | |
luvletter2333 | 7fe115e83c | |
luvletter2333 | 7f43e7f173 | |
xaxtix | 1ac56c671e | |
xaxtix | 8bb3438f62 | |
xaxtix | 7554c9b726 | |
xaxtix | 116092e32d | |
xaxtix | 810bc4ae67 | |
xaxtix | 51e9947527 | |
xaxtix | c48aa4f3c9 | |
xaxtix | fb2d9f90bc | |
xaxtix | 135e3cc766 | |
xaxtix | 2628a58147 | |
xaxtix | 17283cea01 | |
xaxtix | f7f9363423 | |
luvletter2333 | 5890d96dda | |
luvletter2333 | c71626f80e | |
luvletter2333 | adcafbfc6d | |
luvletter2333 | 12bbf436a1 | |
luvletter2333 | 7ef3a53a2f | |
luvletter2333 | 5aa483a795 | |
James Clef | 8f4e6d9428 | |
luvletter2333 | 69c6fce26b | |
luvletter2333 | 27ced58f8c | |
luvletter2333 | f11f97bd2e | |
luvletter2333 | 2eb50eca97 | |
luvletter2333 | 2e15f20707 | |
luvletter2333 | 92d0a8e072 | |
luvletter2333 | 8673b032af | |
luvletter2333 | 85688e84af | |
luvletter2333 | 355df746a1 | |
xaxtix | 1c03d75ea8 | |
xaxtix | 5a31f93ec2 | |
xaxtix | 3ccf875b1b | |
xaxtix | 2c71f6c92b | |
xaxtix | 03e899e4ec | |
xaxtix | b73fc8de38 | |
luvletter2333 | 2077f74a85 | |
luvletter2333 | 89dee06649 | |
luvletter2333 | 17546df4bb | |
luvletter2333 | 9d4ce33277 | |
luvletter2333 | 07b9af621d | |
luvletter2333 | 2984ee4ef3 | |
luvletter2333 | f56b39d56d | |
luvletter2333 | 8c043db2ce | |
luvletter2333 | 85ff913c2b | |
luvletter2333 | 9ed10404e5 | |
luvletter2333 | b95fa5971d | |
luvletter2333 | 6b59317fa6 | |
xaxtix | 0e17caa7a6 | |
xaxtix | 97ab6c63c8 | |
xaxtix | 9bd017a72a | |
xaxtix | 4951061dde | |
luvletter2333 | 25a1933416 | |
xaxtix | 23118a4a6c | |
xaxtix | a2981ddd29 | |
xaxtix | 342e681114 | |
xaxtix | 67b1fd17e6 | |
luvletter2333 | 079b3bb355 | |
luvletter2333 | 46f7f843b1 | |
luvletter2333 | 0b68460d21 | |
luvletter2333 | 50f2b934c5 | |
luvletter2333 | a5393e9e9d | |
luvletter2333 | 68708c8ff3 | |
luvletter2333 | de75de1f2a | |
luvletter2333 | a4aa5f8099 | |
luvletter2333 | 9af95477cc | |
luvletter2333 | 5e4e11dbcd | |
luvletter2333 | 905cf0a7b3 | |
luvletter2333 | 4c8b0b14fa | |
xaxtix | e9a35cea54 | |
xaxtix | 11edd5ee0d | |
xaxtix | abb896635f | |
rhuba8324 | 086b1a6b1c | |
Hykilpikonna | 2340eea7e9 | |
Furkan Karcıoğlu | e653a90ebd | |
luvletter2333 | 2b9cdaab4f | |
luvletter2333 | ed3ba3dcb5 | |
luvletter2333 | 0aaaa283cf | |
luvletter2333 | e59512fa7c | |
luvletter2333 | 6bf77388d7 | |
luvletter2333 | 92f75541ec | |
luvletter2333 | 4f17a21015 | |
luvletter2333 | 70bb838901 | |
luvletter2333 | a7c7030271 | |
luvletter2333 | 4ca3b95c0c | |
luvletter2333 | 7d2d1d12aa | |
luvletter2333 | bd9483a80f | |
luvletter2333 | 575fd5a684 | |
luvletter2333 | 7d1f137c21 | |
luvletter2333 | db06bb66f4 | |
luvletter2333 | 8b2dca3ea7 | |
luvletter2333 | 6c5fae9fae | |
xaxtix | 43fe75b46b | |
xaxtix | 172335d189 | |
xaxtix | b1aa2020e5 | |
luvletter2333 | 41a4743870 | |
luvletter2333 | bfb3168c36 | |
luvletter2333 | dcff475b34 | |
luvletter2333 | 68ae4ede1f | |
luvletter2333 | 6f1f65d1ac | |
luvletter2333 | bca854c8af | |
luvletter2333 | 0a5c733149 | |
luvletter2333 | 0462fe9875 | |
luvletter2333 | e70805c398 | |
luvletter2333 | e879e46f09 | |
luvletter2333 | 8f18d21c78 | |
luvletter2333 | 4151c906a2 | |
Next Alone | bcec381654 | |
luvletter2333 | 6ccb87a96e | |
Next Alone | fd452a03ac | |
luvletter2333 | b87cd3624d | |
luvletter2333 | 93a286ba44 | |
luvletter2333 | 383be2281f | |
luvletter2333 | 271b0dc136 | |
luvletter2333 | 66eb744fc7 | |
luvletter2333 | 3c1d1bf7cd | |
luvletter2333 | 27cb3dc403 | |
luvletter2333 | db86538019 | |
luvletter2333 | ef9e0aa3d5 | |
luvletter2333 | 36ce0178f6 | |
luvletter2333 | c00cac6b28 | |
luvletter2333 | de6d5e01f3 | |
luvletter2333 | 946f24c45b | |
luvletter2333 | 31abbbbddd | |
luvletter2333 | 8f4741f8ab | |
luvletter2333 | e8accc449d | |
luvletter2333 | c71f3240a3 | |
luvletter2333 | b2a57d7c02 | |
luvletter2333 | ec0caec469 | |
luvletter2333 | f8a72d335e | |
luvletter2333 | 11f055d23f | |
luvletter2333 | de2d16705a | |
xtaodada | a3e489c2f9 | |
xtaodada | 7595ba071e | |
luvletter2333 | 7dcb3abadc | |
luvletter2333 | f002ddc261 | |
luvletter2333 | d7e8ce80e3 | |
luvletter2333 | 6e17a698e0 | |
luvletter2333 | ac8950ad0f | |
luvletter2333 | 8e25de7902 | |
luvletter2333 | c15d817e81 | |
Next Alone | 8f3d631d4f | |
Next Alone | 5f9c66e5be | |
Next Alone | eab8088cae | |
luvletter2333 | d0e1ebd029 | |
luvletter2333 | 77d0e82cb0 | |
luvletter2333 | a0f3ad497d | |
luvletter2333 | 4f85d02518 | |
luvletter2333 | 531f4f9ba9 | |
xaxtix | 6cb1cdf898 | |
luvletter2333 | 0a05e0476b | |
luvletter2333 | 73cb11f883 | |
xaxtix | 43401a515c | |
xaxtix | 0a3abc5158 | |
luvletter2333 | 2a317b62ac | |
luvletter2333 | 8057580313 | |
luvletter2333 | 018c93db24 | |
luvletter2333 | 1c3495c92e | |
luvletter2333 | 780dc4d5b6 | |
luvletter2333 | 00f2a0271c | |
xaxtix | 4a95c2fc1f | |
luvletter2333 | 47ba651ce1 | |
luvletter2333 | 58d91d99aa | |
luvletter2333 | e44072e04b | |
luvletter2333 | 013a7aaa89 | |
luvletter2333 | 1cf9174b75 | |
luvletter2333 | 23952a1dd5 | |
xaxtix | 96dce2c9aa | |
luvletter2333 | e3fcc75eca | |
luvletter2333 | 21563770fc | |
luvletter2333 | 067deb365d | |
luvletter2333 | 06df7442ef | |
hhn1611245010 | 6aa94cff52 | |
luvletter2333 | 37c2793e69 | |
luvletter2333 | fa99e892bc | |
xaxtix | 32aef72421 | |
xaxtix | 136c9582a8 | |
xaxtix | 8283c12364 | |
xaxtix | 8309ac8d7c | |
luvletter2333 | 10016b3e49 | |
xaxtix | 1e50785b90 | |
luvletter2333 | b16a3bb372 | |
Weblate (bot) | 0b83d38bba | |
luvletter2333 | a11fc83509 | |
luvletter2333 | 9115bb9569 | |
ChristinaLee | 51a3dad697 | |
luvletter2333 | 9023f54a10 | |
luvletter2333 | 8d5ef289a2 | |
luvletter2333 | 324c39dded | |
gao_cai_sheng | b709d28bff | |
NatsumeMio | 88ef26f33a | |
NatsumeMio | 9e7fd90610 | |
NatsumeMio | 8f54fb267d | |
NatsumeMio | 1c123dedc6 | |
luvletter2333 | 55024ce8bb | |
Legend | 0aabf18b0c | |
luvletter2333 | 9969037be2 | |
luvletter2333 | ff45bdac1e | |
luvletter2333 | 79c6d00fbb | |
luvletter2333 | 538e82ff01 | |
luvletter2333 | 0ed8ff1ed3 | |
luvletter2333 | 00f7b2c892 | |
luvletter2333 | 2f86e87899 | |
luvletter2333 | a6c39637da | |
xaxtix | 0abe4541dd | |
luvletter2333 | 1b78fc6cf1 | |
luvletter2333 | 8cf8776235 | |
luvletter2333 | 4b010e3b11 | |
luvletter2333 | c1a21028e4 | |
luvletter2333 | de17e63f2a | |
luvletter2333 | 7a29fe9d2d | |
luvletter2333 | 8de1a84dc0 | |
luvletter2333 | c26b65bee9 | |
luvletter2333 | 8311829a9b | |
luvletter2333 | fa5b44b51e | |
luvletter2333 | 3b8cf4fde4 | |
xaxtix | 5d5527525f | |
xaxtix | d30f796d8c | |
luvletter2333 | 86c7a6d0be | |
luvletter2333 | 7e03bca531 | |
luvletter2333 | 4a1e66b96b | |
luvletter2333 | d3511e568b | |
luvletter2333 | 6d4e8a06c8 | |
luvletter2333 | be3ff350fb | |
luvletter2333 | 1c57ed952e | |
luvletter2333 | 9fd741ffa0 | |
luvletter2333 | 959f9dfc26 | |
luvletter2333 | 8a999c6aaf | |
luvletter2333 | b10b2e0f7c | |
luvletter2333 | 34d78450c7 | |
luvletter2333 | f406f07718 | |
luvletter2333 | 1bece71050 | |
luvletter2333 | c19229663f | |
luvletter2333 | ab0c0ed4d8 | |
luvletter2333 | 6512972f30 | |
luvletter2333 | f438230f13 | |
luvletter2333 | 32066ecf1f | |
xaxtix | 3708e9847a | |
xaxtix | b5694a1ca6 | |
luvletter2333 | 0379caa36f | |
luvletter2333 | 92bb673a32 | |
xaxtix | 3b4919a542 | |
luvletter2333 | 8a3dc467d3 | |
luvletter2333 | 016bdc4a7d | |
luvletter2333 | d01678de23 | |
luvletter2333 | 47724d4d61 | |
luvletter2333 | ba9f84efee | |
luvletter2333 | 62c7c8b5cf | |
luvletter2333 | c811f7d06b | |
luvletter2333 | 6ca737b4e1 | |
luvletter2333 | 672c7e8688 | |
luvletter2333 | 8d07f4d1ad | |
luvletter2333 | c706d2c2c5 | |
luvletter2333 | be79f8f6de | |
luvletter2333 | c60fd69b04 | |
xaxtix | 146cea26cd | |
luvletter2333 | db1ba95ae5 | |
luvletter2333 | b1d798c8d3 | |
luvletter2333 | 83e8a1b61a | |
luvletter2333 | d4e8d513c4 | |
luvletter2333 | cdc56f97e7 | |
luvletter2333 | d54dd02b59 | |
luvletter2333 | e36c89be5a | |
gao_cai_sheng | fd0ea5b158 | |
luvletter2333 | 00ae420abf | |
luvletter2333 | 20f3658279 | |
luvletter2333 | 2a19e63c62 | |
xaxtix | a1817ea7d3 | |
xaxtix | ee217d739f | |
xaxtix | ad05eea682 | |
luvletter2333 | 040500f1d9 | |
luvletter2333 | 3eaa650947 | |
luvletter2333 | ac308681c6 | |
luvletter2333 | fef56f8a49 | |
luvletter2333 | d041495b30 | |
luvletter2333 | 2c4fdbc001 | |
luvletter2333 | 91857d5a37 | |
luvletter2333 | a6457cac77 | |
luvletter2333 | 7aab8fad6d | |
luvletter2333 | d3ff18c668 | |
luvletter2333 | d67c5c27b9 | |
luvletter2333 | 76ef6ac602 | |
luvletter2333 | 7a63017f54 | |
luvletter2333 | 1d3011ea58 | |
luvletter2333 | b848e5e666 | |
luvletter2333 | 9861e319f4 | |
luvletter2333 | 8618ac484b | |
luvletter2333 | e8e446f586 | |
luvletter2333 | 632b36170c | |
luvletter2333 | cf712d9641 | |
luvletter2333 | 200a359dd7 | |
luvletter2333 | f64c20581d | |
luvletter2333 | b1dce3510f | |
luvletter2333 | e1c74d2aaf | |
luvletter2333 | 2456ab21f1 | |
luvletter2333 | c4afcbf6f2 | |
luvletter2333 | 6a69aa0e9c | |
luvletter2333 | e46ea1135e | |
6666666 | 91a4b200dd | |
xaxtix | f0f858ad3f | |
luvletter2333 | 56a79e2f35 | |
Radshasak Paitoonlerd | 3f410a2017 | |
luvletter2333 | 2de5fba21f | |
Radshasak Paitoonlerd | 66ce1f0fb7 | |
luvletter2333 | 2a6708a4cc | |
luvletter2333 | 2237ece95d | |
luvletter2333 | 130230f081 | |
luvletter2333 | f4d4851672 | |
luvletter2333 | 47d3372766 | |
luvletter2333 | 2288606583 | |
luvletter2333 | f973694b41 | |
luvletter2333 | 6249aba960 | |
luvletter2333 | e78748b6b8 | |
Denys Nykula | 865bf40424 | |
Denys Nykula | 270143347d | |
Denys Nykula | a0f23bad7d | |
Denys Nykula | a21fbf2dc6 | |
FW | 0e7240ed97 | |
araccaine | 0aeec8b717 | |
FW | d7c7e5d63c | |
bruh | ff11feb1a6 | |
qqqq1 | 896904b499 | |
qqqq1 | 4cb2b02b4d | |
qqqq1 | e44ac90834 | |
xaxtix | ad6629f6fc | |
Sam | 86dd5c2a6e | |
Traditional Chinese | a3ab5bfa3c | |
Traditional Chinese | 9df9a377bf | |
Traditional Chinese | befd389536 | |
ssantos | 26f90bdb61 | |
Reza Esmaeili | a8914934c7 | |
Reza Esmaeili | c36ac8299e | |
luvletter2333 | a413352c45 | |
luvletter2333 | b472662a3f | |
luvletter2333 | 097acd41c7 | |
luvletter2333 | 50ed8cfb8b | |
luvletter2333 | 305c7fe18d | |
luvletter2333 | c39ab4ce95 | |
luvletter2333 | 785a883f4e | |
luvletter2333 | 0c53f2b9ed | |
luvletter2333 | 9870b8e343 | |
luvletter2333 | 7474cd9718 | |
xaxtix | c1c2ebaf46 | |
xaxtix | 9e740dfd4d | |
Luv Letter | 1b5efe7178 | |
luvletter2333 | d0bf834786 | |
luvletter2333 | 2584e307a6 | |
luvletter2333 | fdb6ee227a | |
luvletter2333 | 4c8c6de621 | |
luvletter2333 | 21e8c8432e | |
Luv Letter | 3cf4eb12f7 | |
Luv Letter | 4e7e596959 | |
luvletter2333 | e4924c245a | |
luvletter2333 | ecb6ec1c8e | |
luvletter2333 | 65f7479c7c | |
thermatk | ef1107f2f5 | |
thermatk | 0bed4a37d0 | |
luvletter2333 | 6f01267133 | |
luvletter2333 | be93f69a31 | |
luvletter2333 | 67703b1717 | |
luvletter2333 | 5f41f3f0cb | |
luvletter2333 | cde8d42dc7 | |
luvletter2333 | bb2acb6a29 | |
luvletter2333 | 6e60b3ab64 | |
luvletter2333 | 6666c0d566 | |
luvletter2333 | 99d38cb371 | |
luvletter2333 | 72cb3361e1 | |
luvletter2333 | eacd2894ca | |
luvletter2333 | 1d46cd930f | |
luvletter2333 | 23f7d3f443 | |
luvletter2333 | 7c5a12e340 | |
luvletter2333 | 663dc36f12 | |
luvletter2333 | bc309b2104 | |
Алексей Муратов | c25a49894b | |
Алексей Муратов | d9c968cbf4 | |
xaxtix | 4a43f809b3 | |
luvletter2333 | dac6bc4003 | |
luvletter2333 | 159ced5544 | |
luvletter2333 | 0aa89c87be | |
luvletter2333 | b8e0cee4cb | |
luvletter2333 | e8484a26a1 | |
luvletter2333 | 14d60206f5 | |
luvletter2333 | b16412e075 | |
luvletter2333 | 534d30325e | |
luvletter2333 | 5d800050de | |
luvletter2333 | 246e4823ad | |
arm64v8a | e7d45120ba | |
NekoInverter | e7b9b95371 | |
NekoInverter | 9424f76de0 | |
NekoInverter | 4e91347163 | |
xaxtix | dee3203dfa | |
arm64v8a | 3533f7b61e | |
luvletter2333 | d938d7ff9b | |
xaxtix | 3477d72367 | |
xaxtix | dcf1c6d4c3 | |
xaxtix | 64c32ace43 | |
arm64v8a | 6d4984b864 | |
luvletter2333 | 2432283df7 | |
luvletter2333 | 7e6e1498ec | |
luvletter2333 | 5dcfafeca4 | |
luvletter2333 | 334294d683 | |
luvletter2333 | 1e31c91623 | |
luvletter2333 | 2354bc0989 | |
luvletter2333 | 0c3a27f20b | |
luvletter2333 | a6f4408cd7 | |
luvletter2333 | 245eca618b | |
luvletter2333 | 3522b51b18 | |
luvletter2333 | acd5961324 | |
Алексей Муратов | 07ec4d40d5 | |
arm64v8a | 1ec3c9de1a | |
arm64v8a | 04e62a0c0a | |
arm64v8a | 762a7b43be | |
arm64v8a | 657f4bd66c | |
arm64v8a | a5c29444a7 | |
arm64v8a | 1014cfd48d | |
arm64v8a | f4c5e5d919 | |
arm64v8a | 34cc2b6e91 | |
luvletter2333 | 3c8ea5d10b | |
luvletter2333 | fb3a2b88fd | |
luvletter2333 | a8b667084e | |
aarch64 | 9e2f8c6754 | |
luvletter2333 | 59a5b6d633 | |
luvletter2333 | 1c60992a42 | |
arm64v8a | f13d267d32 | |
arm64v8a | d09b2201e5 | |
aarch64 | f7c18a91a4 | |
arm64v8a | 3ff59f4a7d | |
arm64v8a | 158fc41032 | |
proletarius101 | 1081f036c3 | |
proletarius101 | 302e658b0e | |
luvletter2333 | 99c7ba1302 | |
luvletter2333 | fb0a01b4d1 | |
luvletter2333 | 6cb7ce9432 | |
luvletter2333 | 7ae80a380c | |
luvletter2333 | a932196248 | |
luvletter2333 | 087154ed02 | |
arm64v8a | 87e5cb5211 | |
luvletter2333 | 1a384e7819 | |
luvletter2333 | d2e681675d | |
luvletter2333 | 078720aeeb | |
arm64v8a | edd2b3f224 | |
luvletter2333 | 3af0d658e4 | |
luvletter2333 | 6aa2a01b76 | |
luvletter2333 | c245244b29 | |
luvletter2333 | b68bb4932c | |
xaxtix | 23b70a3882 | |
arm64v8a | cb22a70c96 | |
arm64v8a | ca671a7c01 | |
arm64v8a | 4a12bad17c | |
arm64v8a | dd007a01e7 | |
arm64v8a | ddf8e8b7ae | |
arm64v8a | 783aa55c5a | |
arm64v8a | 55edb6bde4 | |
arm64v8a | ac47e574e0 | |
arm64v8a | 3c8781c7de | |
arm64v8a | 4eacbcf38f | |
luvletter2333 | 9dee7ae6d2 | |
luvletter2333 | a097f87677 | |
luvletter2333 | c139e60d06 | |
arm64v8a | 575dc800c2 | |
arm64v8a | a08f0973a4 | |
arm64v8a | dea17045af | |
arm64v8a | 23e1b288b7 | |
xaxtix | 3b971647e2 | |
xaxtix | 44be21dd0f | |
arm64v8a | 28b2b53012 | |
arm64v8a | f3510d25cd | |
arm64v8a | 0637f83540 | |
arm64v8a | 9828ce5de6 | |
arm64v8a | 5696cc55ab | |
arm64v8a | d0bb8abd61 | |
aarch64 | 2ae3c318ef | |
arm64v8a | 771a87b40e | |
arm64v8a | 4471aa430a | |
arm64v8a | ecac2aaa96 | |
arm64v8a | 7a9c83d2b8 | |
arm64v8a | e1098169b6 | |
arm64v8a | 74a2518785 | |
xaxtix | 1d379b9a7b | |
luvletter2333 | 6400ef3a86 | |
luvletter2333 | 6233580e24 | |
arm64v8a | a5ce6c5b91 | |
arm64v8a | 3f4ff5c9ff | |
arm64v8a | c972890ddd | |
Luv Letter | ef0f3cd542 | |
arm64v8a | 099ecfbae1 | |
arm64v8a | bcfbe63b3d | |
arm64v8a | 907f0032ca | |
N | de604298a3 | |
luvletter2333 | 48f9ef024a | |
luvletter2333 | 6aeb6f679e | |
arm64v8a | 837a6543e0 | |
arm64v8a | 11ebf4e639 | |
arm64v8a | 9f288f8999 | |
arm64v8a | eaffdc28ad | |
luvletter2333 | 34c63b4991 | |
luvletter2333 | ef9a38a104 | |
luvletter2333 | af9c719d69 | |
luvletter2333 | 054494a475 | |
arm64v8a | dbc331947f | |
arm64v8a | cf5115cafa | |
arm64v8a | c2bc81ac5a | |
arm64v8a | de5573c5ad | |
aarch64 | 9f686460c2 | |
arm64v8a | 2e9f810e4d | |
arm64v8a | 4ff6df9f9a | |
arm64v8a | c37dfa2831 | |
arm64v8a | 44d546c170 | |
arm64v8a | f4c919d593 | |
arm64v8a | da8d2942f0 | |
arm64v8a | 26f3498957 | |
arm64v8a | d62d686e6c | |
arm64v8a | a60e43c704 | |
arm64v8a | b62eed4c9c | |
arm64v8a | edb9fc5501 | |
arm64v8a | 81c43686ca | |
qwq233 | 9577c1e319 | |
luvletter2333 | d8105a7223 | |
luvletter2333 | d491dd5331 | |
luvletter2333 | 9c31adf0db | |
luvletter2333 | c85b411a3e | |
luvletter2333 | 78a8692a67 | |
luvletter2333 | 77e081f98c | |
luvletter2333 | e4559ae03e | |
luvletter2333 | 97d13b90a4 | |
xaxtix | 09f95ec069 | |
luvletter2333 | 02a95a3ce9 | |
luvletter2333 | a7c1f6958a | |
luvletter2333 | bb58092263 | |
luvletter2333 | 36ed1295c9 | |
luvletter2333 | 99858655f1 | |
luvletter2333 | 6ccaf3ff78 | |
xaxtix | 7e91c949bc | |
xaxtix | 58aded63e9 | |
世界 | a36a9c5980 | |
世界 | 8c0def7556 | |
luvletter2333 | 7cc94fa9ea | |
luvletter2333 | 87a6a5b5e9 | |
luvletter2333 | 219549f0fd | |
luvletter2333 | 5c57c63af0 | |
luvletter2333 | cead4be034 | |
luvletter2333 | d5188d73f9 | |
luvletter2333 | 03db127ae1 | |
luvletter2333 | 972d5b0430 | |
luvletter2333 | b3e16dca87 | |
luvletter2333 | 5ca6f284e4 | |
luvletter2333 | b6c695022e | |
luvletter2333 | 1d25b61d10 | |
luvletter2333 | c063ee58b5 | |
luvletter2333 | 2b1309bb69 | |
luvletter2333 | 0656bd2ef8 | |
luvletter2333 | bcff1d141e | |
luvletter2333 | 2292c48ea1 | |
luvletter2333 | f6d1807fbe | |
luvletter2333 | 65548549fd | |
xaxtix | 42feed0f42 | |
luvletter2333 | 43001f700a | |
xaxtix | 418f478a62 | |
luvletter2333 | af3b235626 | |
luvletter2333 | 8a080aa535 | |
luvletter2333 | 72d85b8a80 | |
luvletter2333 | c62714af5a | |
luvletter2333 | be1c61363e | |
luvletter2333 | 007b632c51 | |
luvletter2333 | 9125d6e0f3 | |
luvletter2333 | 54295812ec | |
luvletter2333 | bb2dff85cd | |
luvletter2333 | 2e99b4e41d | |
luvletter2333 | 986762f504 | |
DrKLO | 368822d20f | |
luvletter2333 | 087c7b850e | |
luvletter2333 | 181fba47ae | |
luvletter2333 | 3cb50b03be | |
luvletter2333 | 87eab8f0df | |
luvletter2333 | 891a10e69c | |
luvletter2333 | 94a130346c | |
luvletter2333 | 50ee1ecd98 | |
luvletter2333 | 31a35ce721 | |
luvletter2333 | f0761cb93c | |
luvletter2333 | df23ecad49 | |
luvletter2333 | ad62860829 | |
luvletter2333 | 5b8479f679 | |
luvletter2333 | cd6cbd8afe | |
luvletter2333 | f527e84247 | |
luvletter2333 | e8179cd090 | |
luvletter2333 | 3822b50f4b |
|
@ -0,0 +1,164 @@
|
|||
import requests
|
||||
import sys
|
||||
import os
|
||||
|
||||
def read_env(key):
|
||||
if key in os.environ:
|
||||
return os.environ[key]
|
||||
else:
|
||||
print(f"failed to read env {key}")
|
||||
return ""
|
||||
|
||||
APK_FOLDER = "apks"
|
||||
API_PREFIX = "http://127.0.0.1:38118/bot" + os.environ["BOT_TOKEN"] + "/"
|
||||
|
||||
BOT_TARGET = read_env("BOT_TARGET")
|
||||
ADMIN_USERID = BOT_TARGET.replace("-100","")
|
||||
|
||||
VERSION_NAME = read_env("VERSION_NAME")
|
||||
VERSION_CODE = read_env("VERSION_CODE")
|
||||
COMMIT_HASH = read_env("GITHUB_SHA")
|
||||
COMMIT_MESSAGE = read_env("COMMIT_MESSAGE")
|
||||
|
||||
APK_CHANNEL_ID = "@NekoXApks"
|
||||
UPDATE_CHANNEL_ID = "@NekogramX"
|
||||
UPDATE_METADATA_CHANNEL_ID = "@nekox_update_metadata"
|
||||
CI_CHANNEL_ID = "@NekoX_CI"
|
||||
|
||||
|
||||
def addEntity(entities, origin_str, en_type, content, url = None) -> str:
|
||||
origin_len = len(origin_str)
|
||||
entity = {
|
||||
"type": en_type,
|
||||
"offset": origin_len,
|
||||
"length": len(content)
|
||||
}
|
||||
if url:
|
||||
entity["url"] = url
|
||||
entities.append(entity)
|
||||
return content
|
||||
|
||||
|
||||
def waitReply(mid):
|
||||
last_update = 0
|
||||
while True:
|
||||
print(f"Waiting reply for {mid} offset {last_update}...")
|
||||
resp = requests.post(API_PREFIX + "getUpdates", json={"allowed_updates":["message"], "timeout": 20, "offset": last_update + 1})
|
||||
resp = resp.json()
|
||||
if not resp["ok"]:
|
||||
continue
|
||||
resp = resp["result"]
|
||||
for update in resp:
|
||||
if 'message' in update:
|
||||
msg = update["message"]
|
||||
if 'from' in msg and str(msg['from']["id"]) == ADMIN_USERID:
|
||||
if 'reply_to_message' in msg and str(msg["reply_to_message"]["message_id"]) == str(mid):
|
||||
return msg
|
||||
for update in resp:
|
||||
last_update = max(last_update, update["update_id"])
|
||||
|
||||
|
||||
def sendMessage(message, user_id = BOT_TARGET, entities = None) -> int:
|
||||
data = {
|
||||
"chat_id" : user_id,
|
||||
"text": message,
|
||||
"entities": entities
|
||||
}
|
||||
print(message)
|
||||
print(entities)
|
||||
resp = requests.post(API_PREFIX + "sendMessage", json=data).json()
|
||||
print(resp)
|
||||
return int(resp["result"]["message_id"])
|
||||
|
||||
|
||||
def sendDocument(user_id, path, message = "", entities = None):
|
||||
files = {'document': open(path, 'rb')}
|
||||
data = {'chat_id': user_id,
|
||||
'caption': message,
|
||||
'parse_mode': 'Markdown',
|
||||
'caption_entities': entities}
|
||||
response = requests.post(API_PREFIX + "sendDocument", files=files, data=data)
|
||||
print(response.json())
|
||||
|
||||
|
||||
def sendRelease():
|
||||
apks = os.listdir(APK_FOLDER)
|
||||
apks.sort()
|
||||
print(apks)
|
||||
|
||||
# read message from admin
|
||||
mid = sendMessage(f"Please reply the release message for the version {VERSION_NAME},{VERSION_CODE}:", user_id=BOT_TARGET)
|
||||
admin_resp = waitReply(mid)
|
||||
|
||||
# send message and apks to APK channel
|
||||
message = f"=== {VERSION_NAME} ==="
|
||||
apk_channel_first_id = sendMessage(message, user_id=APK_CHANNEL_ID)
|
||||
|
||||
for apk in apks:
|
||||
path = os.path.join(APK_FOLDER, apk)
|
||||
sendDocument(user_id=APK_CHANNEL_ID, path=path)
|
||||
|
||||
# generate release message and send to update channel
|
||||
entities = []
|
||||
text = ""
|
||||
text += addEntity(entities, text, "hashtag", f"#{'PRE_RELEASE' if 'preview' in VERSION_NAME else 'RELEASE'}")
|
||||
text += " "
|
||||
text += addEntity(entities, text, "text_link", " ", f'https://t.me/{APK_CHANNEL_ID.replace("@","")}/{apk_channel_first_id + 1}')
|
||||
text += " "
|
||||
text += addEntity(entities, text, "bold", VERSION_NAME)
|
||||
text += "\n\n"
|
||||
if "entities" not in admin_resp:
|
||||
admin_resp["entities"] = list()
|
||||
resp_entities = admin_resp["entities"]
|
||||
for en in resp_entities:
|
||||
copy = en.copy()
|
||||
copy["offset"] += len(text)
|
||||
entities.append(copy)
|
||||
text += admin_resp["text"]
|
||||
text += "\n\n"
|
||||
text += addEntity(entities, text, "text_link", "GitHub Release", f"https://github.com/NekoX-Dev/NekoX/releases/{VERSION_NAME}")
|
||||
text += " | "
|
||||
text += addEntity(entities, text, "text_link", "Apks", f'https://t.me/{APK_CHANNEL_ID.replace("@","")}/{apk_channel_first_id}')
|
||||
text += " | "
|
||||
text += addEntity(entities, text, "text_link", "Check Update", "tg://update/")
|
||||
|
||||
sendMessage(text, user_id=UPDATE_CHANNEL_ID, entities=entities)
|
||||
|
||||
# send release message to metadata channel
|
||||
mid = sendMessage(admin_resp["text"], user_id=UPDATE_METADATA_CHANNEL_ID, entities=admin_resp["entities"])
|
||||
meta_msg = f"{VERSION_NAME},{VERSION_CODE},{apk_channel_first_id},{mid}"
|
||||
sendMessage(meta_msg, user_id=UPDATE_METADATA_CHANNEL_ID)
|
||||
|
||||
|
||||
def sendCIRelease():
|
||||
apks = os.listdir(APK_FOLDER)
|
||||
apks.sort()
|
||||
apk = os.path.join(APK_FOLDER, apks[0])
|
||||
entities = []
|
||||
message = f"CI Build\n\n{COMMIT_MESSAGE}\n\n"
|
||||
message += addEntity(entities, message, "text_link", COMMIT_HASH[0:8], f"https://github.com/NekoX-Dev/NekoX/commit/{COMMIT_HASH}")
|
||||
sendDocument(user_id=CI_CHANNEL_ID, path = apk, message=message, )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(sys.argv)
|
||||
if len(sys.argv) != 2:
|
||||
print("Run Type: release, ci, debug")
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
exit(1)
|
||||
mode = sys.argv[1]
|
||||
if mode == "release":
|
||||
sendRelease()
|
||||
elif mode == "ci":
|
||||
if COMMIT_MESSAGE.startswith("ci"):
|
||||
CI_CHANNEL_ID = BOT_TARGET
|
||||
sendCIRelease()
|
||||
elif mode == "debug":
|
||||
APK_CHANNEL_ID = "@test_channel_nekox"
|
||||
UPDATE_CHANNEL_ID = "@test_channel_nekox"
|
||||
UPDATE_METADATA_CHANNEL_ID = "@test_channel_nekox"
|
||||
sendRelease()
|
||||
else:
|
||||
print("unknown mode")
|
||||
|
|
@ -12,6 +12,10 @@ on:
|
|||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
artifacts:
|
||||
description: 'y, if need artifacts'
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
ffmpeg:
|
||||
|
@ -21,14 +25,17 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Fetch Status
|
||||
run: git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
git submodule status TMessagesProj/jni/libvpx > libvpx_status
|
||||
- name: FFmpeg Cache
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/jni/ffmpeg/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'TMessagesProj/jni/*_ffmpeg.sh') }}
|
||||
TMessagesProj/jni/libvpx/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'libvpx_status', 'TMessagesProj/jni/*ffmpeg*.sh', 'TMessagesProj/jni/*libvpx*.sh', 'TMessagesProj/jni/patches/ffmpeg/*') }}
|
||||
- name: Setup Android SDK Tools
|
||||
uses: android-actions/setup-android@v2
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
|
@ -41,6 +48,8 @@ jobs:
|
|||
- name: Native Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
sudo apt-get install yasm -y
|
||||
./run init libs libvpx
|
||||
./run init libs ffmpeg
|
||||
boringssl:
|
||||
name: Native Build (BoringSSL)
|
||||
|
@ -90,6 +99,7 @@ jobs:
|
|||
- name: Fetch Status
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
git submodule status TMessagesProj/jni/libvpx > libvpx_status
|
||||
git submodule status TMessagesProj/jni/boringssl > boringssl_status
|
||||
- name: Native Cache
|
||||
id: cache
|
||||
|
@ -108,7 +118,8 @@ jobs:
|
|||
with:
|
||||
path: |
|
||||
TMessagesProj/jni/ffmpeg/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'TMessagesProj/jni/*_ffmpeg.sh') }}
|
||||
TMessagesProj/jni/libvpx/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'libvpx_status', 'TMessagesProj/jni/*ffmpeg*.sh', 'TMessagesProj/jni/*libvpx*.sh', 'TMessagesProj/jni/patches/ffmpeg/*') }}
|
||||
- name: BoringSSL Cache
|
||||
uses: actions/cache@v2
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
|
@ -237,17 +248,18 @@ jobs:
|
|||
flavor:
|
||||
- MiniRelease
|
||||
- MiniReleaseNoGcm
|
||||
- MiniAppleEmojiRelease
|
||||
- MiniAppleEmojiReleaseNoGcm
|
||||
- FullRelease
|
||||
- FullReleaseNoGcm
|
||||
- FullAppleEmojiRelease
|
||||
- FullAppleEmojiReleaseNoGcm
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Android SDK Tools
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;21.4.7075529" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "sdk.dir=${ANDROID_HOME}" > local.properties
|
||||
echo "ndk.dir=${ANDROID_HOME}/ndk/21.4.7075529" >> local.properties
|
||||
- name: Fetch Status
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
|
@ -301,9 +313,7 @@ jobs:
|
|||
run: |
|
||||
sed -i -e "s/16384/6144/g" gradle.properties
|
||||
echo "sdk.dir=${ANDROID_HOME}" >> local.properties
|
||||
echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" >> local.properties
|
||||
- name: Fix Compile SDK 31.0.0
|
||||
run: ./run init env
|
||||
# echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" >> local.properties
|
||||
- name: Gradle cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
|
@ -315,27 +325,209 @@ jobs:
|
|||
export DEBUG_BUILD=true
|
||||
./gradlew TMessagesProj:assemble${{ matrix.flavor }}
|
||||
|
||||
# echo "APK_FILE=$(find TMessagesProj/build/outputs/apk -name '*arm64-v8a*.apk')" >> $GITHUB_ENV
|
||||
# echo "APK_FILE_ARMV7=$(find TMessagesProj/build/outputs/apk -name '*armeabi*.apk')" >> $GITHUB_ENV
|
||||
# echo "APK_FILE_X86=$(find TMessagesProj/build/outputs/apk -name '*x86-*.apk')" >> $GITHUB_ENV
|
||||
# echo "APK_FILE_X64=$(find TMessagesProj/build/outputs/apk -name '*x86_64*.apk')" >> $GITHUB_ENV
|
||||
# - uses: actions/upload-artifact@v2
|
||||
# name: Upload apk (arm64-v8a)
|
||||
# with:
|
||||
# name: NekoX-${{ matrix.flavor }}-arm64-v8a
|
||||
# path: ${{ env.APK_FILE }}
|
||||
# - uses: actions/upload-artifact@v2
|
||||
# name: Upload apk (armeabi-v7a)
|
||||
# with:
|
||||
# name: NekoX-${{ matrix.flavor }}-armeabi-v7a
|
||||
# path: ${{ env.APK_FILE_ARMV7 }}
|
||||
# - uses: actions/upload-artifact@v2
|
||||
# name: Upload apk (x86_64)
|
||||
# with:
|
||||
# name: NekoX-${{ matrix.flavor }}-x86_64
|
||||
# path: ${{ env.APK_FILE_X64 }}
|
||||
# - uses: actions/upload-artifact@v2
|
||||
# name: Upload apk (x86)
|
||||
# with:
|
||||
# name: NekoX-${{ matrix.flavor }}-x86
|
||||
# path: ${{ env.APK_FILE_X86 }}
|
||||
echo "APK_FILE=$(find TMessagesProj/build/outputs/apk -name '*arm64-v8a*.apk')" >> $GITHUB_ENV
|
||||
echo "APK_FILE_ARMV7=$(find TMessagesProj/build/outputs/apk -name '*armeabi*.apk')" >> $GITHUB_ENV
|
||||
echo "APK_FILE_X86=$(find TMessagesProj/build/outputs/apk -name '*x86-*.apk')" >> $GITHUB_ENV
|
||||
echo "APK_FILE_X64=$(find TMessagesProj/build/outputs/apk -name '*x86_64*.apk')" >> $GITHUB_ENV
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: github.event.inputs.artifacts == 'y'
|
||||
name: Upload apk (arm64-v8a)
|
||||
with:
|
||||
name: NekoX-${{ matrix.flavor }}-arm64-v8a
|
||||
path: ${{ env.APK_FILE }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: github.event.inputs.artifacts == 'y'
|
||||
name: Upload apk (armeabi-v7a)
|
||||
with:
|
||||
name: NekoX-${{ matrix.flavor }}-armeabi-v7a
|
||||
path: ${{ env.APK_FILE_ARMV7 }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: github.event.inputs.artifacts == 'y'
|
||||
name: Upload apk (x86_64)
|
||||
with:
|
||||
name: NekoX-${{ matrix.flavor }}-x86_64
|
||||
path: ${{ env.APK_FILE_X64 }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: github.event.inputs.artifacts == 'y'
|
||||
name: Upload apk (x86)
|
||||
with:
|
||||
name: NekoX-${{ matrix.flavor }}-x86
|
||||
path: ${{ env.APK_FILE_X86 }}
|
||||
|
||||
build-dev:
|
||||
name: Gradle Build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'pull_request' && success() }}
|
||||
needs:
|
||||
- native
|
||||
- v2ray
|
||||
- telegram-bot-api
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Android SDK Tools
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;21.4.7075529" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "sdk.dir=${ANDROID_HOME}" > local.properties
|
||||
echo "ndk.dir=${ANDROID_HOME}/ndk/21.4.7075529" >> local.properties
|
||||
- name: Fetch Status
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
git submodule status TMessagesProj/jni/boringssl > boringssl_status
|
||||
git submodule status ss-rust/src/main/rust/shadowsocks-rust > shadowsocks_status
|
||||
git submodule status 'ssr-libev/*' > shadowsocksr_status
|
||||
git submodule status v2ray > v2ray_status
|
||||
- name: Native Cache (armeabi-v7a)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-armeabi-v7a
|
||||
- name: Native Cache (arm64-v8a)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-arm64-v8a
|
||||
- name: Native Cache (x86)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-x86
|
||||
- name: Native Cache (x86_64)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-x86_64
|
||||
- name: V2ray Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/libs/libv2ray.aar
|
||||
key: ${{ hashFiles('bin/libs/v2ray/*', 'v2ray_status') }}
|
||||
- name: Build Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/build
|
||||
TMessagesProj/.cxx
|
||||
ss-rust/build
|
||||
ssr-rust/build
|
||||
openpgp-api/build
|
||||
key: build-cache
|
||||
- name: Configure Gradle
|
||||
run: |
|
||||
sed -i -e "s/16384/6144/g" gradle.properties
|
||||
echo "sdk.dir=${ANDROID_HOME}" >> local.properties
|
||||
# echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" >> local.properties
|
||||
- name: Gradle cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle
|
||||
key: gradle-${{ hashFiles('**/*.gradle') }}
|
||||
- name: Download Telegram Bot API Binary
|
||||
uses: actions/download-artifact@master
|
||||
with:
|
||||
name: telegram-bot-api-binary
|
||||
path: .
|
||||
- name: CI Build
|
||||
run: |
|
||||
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
|
||||
export DEBUG_BUILD=true
|
||||
export NATIVE_TARGET=universal
|
||||
./gradlew TMessagesProj:assembleMiniRelease
|
||||
|
||||
APK=$(find TMessagesProj/build/outputs/apk/mini/release -name 'NekoX*.apk')
|
||||
echo "APK=$APK" >> $GITHUB_ENV
|
||||
VERSION_CODE=$(grep -E "def verCode = ([0-9]+)" TMessagesProj/build.gradle | sed "s/def verCode = //")
|
||||
VERSION_NAME=$(grep -E "def verName = " TMessagesProj/build.gradle | sed "s/def verName = //" | sed "s/\"//g")
|
||||
echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_ENV
|
||||
echo "VERSION_NAME=VERSION_NAME" >> $GITHUB_ENV
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ci-apk
|
||||
path: ${{ env.APK }}
|
||||
upload:
|
||||
name: Upload Apks to Telegram
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-dev
|
||||
- telegram-bot-api
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ci-apk
|
||||
path: apks
|
||||
- name: Download Telegram Bot API Binary
|
||||
uses: actions/download-artifact@master
|
||||
with:
|
||||
name: telegram-bot-api-binary
|
||||
path: .
|
||||
- name: Install Python Environment
|
||||
run: |
|
||||
sudo apt install python3 python3-pip -y
|
||||
pip3 install requests
|
||||
- name: Prepare Upload
|
||||
run: |
|
||||
chmod +x telegram-bot-api-binary
|
||||
function start() {
|
||||
./telegram-bot-api-binary --api-id=21724 --api-hash=3e0cb5efcd52300aec5994fdfc5bdc16 -p 38118 --local 2>&1 > /dev/null &
|
||||
sleep 5
|
||||
}
|
||||
start
|
||||
curl http://127.0.0.1:38118/ || start
|
||||
curl http://127.0.0.1:38118/ || start
|
||||
curl http://127.0.0.1:38118/ || start
|
||||
curl http://127.0.0.1:38118/ || start
|
||||
- name: Upload
|
||||
run: |
|
||||
curl https://raw.githubusercontent.com/NekoX-Dev/NekoX/dev/.github/scripts/upload.py -o upload.py
|
||||
|
||||
export BOT_TOKEN="${{ secrets.HELPER_BOT_TOKEN }}"
|
||||
export BOT_TARGET="${{ secrets.HELPER_BOT_TARGET }}"
|
||||
export COMMIT_MESSAGE="${{ github.event.head_commit.message }}"
|
||||
|
||||
python3 upload.py ci
|
||||
|
||||
pkill telegram-bot
|
||||
|
||||
telegram-bot-api:
|
||||
name: Telegram Bot API
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Clone Telegram Bot API
|
||||
run: |
|
||||
git clone --recursive https://github.com/tdlib/telegram-bot-api.git
|
||||
git status telegram-bot-api >> telegram-bot-api-status
|
||||
- name: Cache Bot API Binary
|
||||
id: cache-bot-api
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: telegram-bot-api-binary
|
||||
key: CI-telegram-bot-api-${{ hashFiles('telegram-bot-api-status') }}
|
||||
- name: Compile Telegram Bot API
|
||||
if: steps.cache-bot-api.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install make git zlib1g-dev libssl-dev gperf cmake g++
|
||||
cd telegram-bot-api
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=.. ..
|
||||
cmake --build . --target install -j$(nproc)
|
||||
cd ../..
|
||||
ls -l telegram-bot-api/bin/telegram-bot-api*
|
||||
cp telegram-bot-api/bin/telegram-bot-api telegram-bot-api-binary
|
||||
- name: Upload Binary
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: telegram-bot-api-binary
|
||||
path: telegram-bot-api-binary
|
||||
|
|
|
@ -33,14 +33,17 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Fetch Status
|
||||
run: git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
git submodule status TMessagesProj/jni/libvpx > libvpx_status
|
||||
- name: FFmpeg Cache
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/jni/ffmpeg/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'TMessagesProj/jni/*_ffmpeg.sh') }}
|
||||
TMessagesProj/jni/libvpx/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'libvpx_status', 'TMessagesProj/jni/*ffmpeg*.sh', 'TMessagesProj/jni/*libvpx*.sh', 'TMessagesProj/jni/patches/ffmpeg/*') }}
|
||||
- name: Setup Android SDK Tools
|
||||
uses: android-actions/setup-android@v2
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
|
@ -53,6 +56,8 @@ jobs:
|
|||
- name: Native Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
sudo apt-get install yasm -y
|
||||
./run init libs libvpx
|
||||
./run init libs ffmpeg
|
||||
boringssl:
|
||||
name: Native Build (BoringSSL)
|
||||
|
@ -103,6 +108,7 @@ jobs:
|
|||
- name: Fetch Status
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
git submodule status TMessagesProj/jni/libvpx > libvpx_status
|
||||
git submodule status TMessagesProj/jni/boringssl > boringssl_status
|
||||
- name: Native Cache
|
||||
id: cache
|
||||
|
@ -121,7 +127,8 @@ jobs:
|
|||
with:
|
||||
path: |
|
||||
TMessagesProj/jni/ffmpeg/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'TMessagesProj/jni/*_ffmpeg.sh') }}
|
||||
TMessagesProj/jni/libvpx/build
|
||||
key: ${{ hashFiles('ffmpeg_status', 'libvpx_status', 'TMessagesProj/jni/*ffmpeg*.sh', 'TMessagesProj/jni/*libvpx*.sh', 'TMessagesProj/jni/patches/ffmpeg/*') }}
|
||||
- name: BoringSSL Cache
|
||||
uses: actions/cache@v2
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
|
@ -253,17 +260,18 @@ jobs:
|
|||
flavor:
|
||||
- FullRelease
|
||||
- FullReleaseNoGcm
|
||||
- FullAppleEmojiRelease
|
||||
- FullAppleEmojiReleaseNoGcm
|
||||
- MiniRelease
|
||||
- MiniReleaseNoGcm
|
||||
- MiniAppleEmojiRelease
|
||||
- MiniAppleEmojiReleaseNoGcm
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Android SDK Tools
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;21.4.7075529" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "sdk.dir=${ANDROID_HOME}" > local.properties
|
||||
echo "ndk.dir=${ANDROID_HOME}/ndk/21.4.7075529" >> local.properties
|
||||
- name: Fetch Status
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
|
@ -316,9 +324,7 @@ jobs:
|
|||
- name: Fix Gradle Memoery
|
||||
run: |
|
||||
sed -i -e "s/16384/6144/g" gradle.properties
|
||||
echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" > local.properties
|
||||
- name: Fix Compile SDK 31.0.0
|
||||
run: ./run init env
|
||||
# echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" > local.properties
|
||||
- name: Gradle cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
|
@ -360,112 +366,80 @@ jobs:
|
|||
name: Upload Release
|
||||
if: github.event.inputs.upload != 'y'
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Donwload Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Release
|
||||
run: |
|
||||
mkdir apks
|
||||
find artifacts -name "*.apk" -exec cp {} apks \;
|
||||
function upload() {
|
||||
for apk in $@; do
|
||||
echo ">> Uploading $apk"
|
||||
curl https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendDocument \
|
||||
-X POST \
|
||||
-F chat_id="${{ secrets.TELEGRAM_CHANNEL }}" \
|
||||
-F document="@$apk" \
|
||||
--silent --show-error --fail >/dev/null &
|
||||
done
|
||||
for job in $(jobs -p); do
|
||||
wait $job || exit 1
|
||||
done
|
||||
}
|
||||
upload apks/NekoX-mini-*-release.apk
|
||||
upload apks/NekoX-mini-*-releaseNoGcm.apk
|
||||
upload apks/NekoX-full-*-release.apk
|
||||
upload apks/NekoX-full-*-releaseNoGcm.apk
|
||||
play:
|
||||
name: Publish to Play Store
|
||||
if: github.event.inputs.play != 'y'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- native
|
||||
- v2ray
|
||||
- shadowsocks
|
||||
- shadowsocksr
|
||||
- build
|
||||
- telegram-bot-api
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Android SDK Tools
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Fetch Status
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Download Telegram Bot API Binary
|
||||
uses: actions/download-artifact@master
|
||||
with:
|
||||
name: telegram-bot-api-binary
|
||||
path: .
|
||||
- name: Prepare Upload
|
||||
run: |
|
||||
git submodule status TMessagesProj/jni/ffmpeg > ffmpeg_status
|
||||
git submodule status TMessagesProj/jni/boringssl > boringssl_status
|
||||
git submodule status ss-rust/src/main/rust/shadowsocks-rust > shadowsocks_status
|
||||
git submodule status 'ssr-libev/*' > shadowsocksr_status
|
||||
git submodule status v2ray > v2ray_status
|
||||
- name: Native Cache (armeabi-v7a)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-armeabi-v7a
|
||||
- name: Native Cache (arm64-v8a)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-arm64-v8a
|
||||
- name: Native Cache (x86)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-x86
|
||||
- name: Native Cache (x86_64)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/src/main/libs
|
||||
key: ${{ hashFiles('TMessagesProj/jni/**', 'ffmpeg_status', 'boringssl_status') }}-x86_64
|
||||
- name: V2ray Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/libs/libv2ray.aar
|
||||
key: ${{ hashFiles('bin/libs/v2ray/*', 'v2ray_status') }}
|
||||
- name: Shadowsocks Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/libs/ss-rust-release.aar
|
||||
key: ${{ hashFiles('shadowsocks_status') }}
|
||||
- name: ShadowsocksR Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
TMessagesProj/libs/ssr-libev-release.aar
|
||||
key: ${{ hashFiles('shadowsocksr_status') }}
|
||||
- name: Fix Gradle Memoery
|
||||
chmod +x telegram-bot-api-binary
|
||||
function start() {
|
||||
./telegram-bot-api-binary -p 38118 --api-id=21724 --api-hash=3e0cb5efcd52300aec5994fdfc5bdc16 --local 2>&1 > /dev/null &
|
||||
sleep 5
|
||||
}
|
||||
start
|
||||
curl http://127.0.0.1:38118/ || start
|
||||
curl http://127.0.0.1:38118/ || start
|
||||
curl http://127.0.0.1:38118/ || start
|
||||
- name: Upload
|
||||
run: |
|
||||
sed -i -e "s/16384/6144/g" gradle.properties
|
||||
echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" > local.properties
|
||||
- name: Fix Compile SDK 31.0.0
|
||||
run: ./run init env
|
||||
- name: Gradle cache
|
||||
mkdir apks
|
||||
find artifacts -name "*.apk" -exec cp {} apks \;
|
||||
curl https://raw.githubusercontent.com/NekoX-Dev/NekoX/dev/.github/scripts/upload.py -o upload.py
|
||||
export BOT_TOKEN="${{ secrets.HELPER_BOT_TOKEN }}"
|
||||
export BOT_TARGET="${{ secrets.HELPER_BOT_TARGET }}"
|
||||
export VERSION_CODE="$(grep -E "def verCode = ([0-9]+)" TMessagesProj/build.gradle | sed "s/def verCode = //")"
|
||||
export VERSION_NAME="$(grep -E "def verName = " TMessagesProj/build.gradle | sed "s/def verName = //" | sed "s/\"//g")"
|
||||
python3 upload.py release
|
||||
pkill telegram-bot
|
||||
- name: Setup upterm session
|
||||
uses: lhotari/action-upterm@v1
|
||||
with:
|
||||
limit-access-to-users: luvletter2333
|
||||
|
||||
telegram-bot-api:
|
||||
name: Telegram Bot API
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Clone Telegram Bot API
|
||||
run: |
|
||||
git clone --recursive https://github.com/tdlib/telegram-bot-api.git
|
||||
git status telegram-bot-api >> telegram-bot-api-status
|
||||
- name: Cache Bot API Binary
|
||||
id: cache-bot-api
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle
|
||||
key: gradle-${{ hashFiles('**/*.gradle') }}
|
||||
- name: Publish to Play Store
|
||||
path: telegram-bot-api-binary
|
||||
key: CI-telegram-bot-api-${{ hashFiles('telegram-bot-api-status') }}
|
||||
- name: Compile Telegram Bot API
|
||||
if: steps.cache-bot-api.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
|
||||
cat > service_account_credentials.json << EOF
|
||||
${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}"
|
||||
EOF
|
||||
sed -i -e 's@<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />@@g' TMessagesProj/src/main/AndroidManifest.xml
|
||||
./gradlew TMessagesProj:publishMiniPlayRelease
|
||||
sudo apt-get update
|
||||
sudo apt-get install make git zlib1g-dev libssl-dev gperf cmake g++
|
||||
cd telegram-bot-api
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=.. ..
|
||||
cmake --build . --target install -j$(nproc)
|
||||
cd ../..
|
||||
ls -l telegram-bot-api/bin/telegram-bot-api*
|
||||
cp telegram-bot-api/bin/telegram-bot-api telegram-bot-api-binary
|
||||
- name: Upload Binary
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: telegram-bot-api-binary
|
||||
path: telegram-bot-api-binary
|
|
@ -9,6 +9,10 @@
|
|||
path = TMessagesProj/jni/boringssl
|
||||
url = https://github.com/google/boringssl
|
||||
ignore = dirty
|
||||
[submodule "TMessagesProj/jni/libvpx"]
|
||||
path = TMessagesProj/jni/libvpx
|
||||
url = https://github.com/webmproject/libvpx
|
||||
ignore = dirty
|
||||
|
||||
[submodule "ss-rust/src/main/rust/shadowsocks-rust"]
|
||||
path = ss-rust/src/main/rust/shadowsocks-rust
|
||||
|
|
30
Dockerfile
30
Dockerfile
|
@ -1,30 +0,0 @@
|
|||
FROM gradle:6.7.1-jdk11
|
||||
|
||||
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/commandlinetools-linux-7302050_latest.zip
|
||||
ENV ANDROID_API_LEVEL android-31
|
||||
ENV ANDROID_BUILD_TOOLS_VERSION 31.0.0
|
||||
ENV ANDROID_HOME /usr/local/android-sdk-linux
|
||||
ENV ANDROID_NDK_VERSION 21.4.7075529
|
||||
ENV ANDROID_VERSION 31
|
||||
ENV ANDROID_NDK_HOME ${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}/
|
||||
ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
|
||||
|
||||
RUN mkdir "$ANDROID_HOME" .android && \
|
||||
cd "$ANDROID_HOME" && \
|
||||
curl -o sdk.zip $ANDROID_SDK_URL && \
|
||||
unzip sdk.zip && \
|
||||
rm sdk.zip
|
||||
|
||||
RUN yes | ${ANDROID_HOME}/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_HOME --licenses
|
||||
RUN $ANDROID_HOME/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_HOME --update
|
||||
RUN $ANDROID_HOME/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_HOME "build-tools;30.0.3" \
|
||||
"build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \
|
||||
"platforms;android-${ANDROID_VERSION}" \
|
||||
"platform-tools" \
|
||||
"ndk;$ANDROID_NDK_VERSION"
|
||||
RUN cp $ANDROID_HOME/build-tools/30.0.3/dx $ANDROID_HOME/build-tools/31.0.0/dx
|
||||
RUN cp $ANDROID_HOME/build-tools/30.0.3/lib/dx.jar $ANDROID_HOME/build-tools/31.0.0/lib/dx.jar
|
||||
ENV PATH ${ANDROID_NDK_HOME}:$PATH
|
||||
ENV PATH ${ANDROID_NDK_HOME}/prebuilt/linux-x86_64/bin/:$PATH
|
||||
|
||||
CMD mkdir -p /home/source/TMessagesProj/build/outputs/apk && mkdir -p /home/source/TMessagesProj/build/outputs/native-debug-symbols && cp -R /home/source/. /home/gradle && cd /home/gradle && gradle assembleRelease && gradle assembleStandalone && cp -R /home/gradle/TMessagesProj/build/outputs/apk/. /home/source/TMessagesProj/build/outputs/apk && cp -R /home/gradle/TMessagesProj/build/outputs/native-debug-symbols/. /home/source/TMessagesProj/build/outputs/native-debug-symbols
|
88
README.md
88
README.md
|
@ -2,9 +2,9 @@
|
|||
|
||||
NekoX is an **free and open source** third-party Telegram client, based on Telegram-FOSS with features added.
|
||||
|
||||
[![Get it on F-Droid](https://i.imgur.com/HDicnzz.png)](https://f-droid.org/packages/nekox.messenger)
|
||||
[中文FAQ](https://github.com/NekoX-Dev/NekoX/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)
|
||||
|
||||
We are no longer able to update versions on the Play Store, check https://telegra.ph/So-they-solved-the-person-who-asked-the-question-04-28 .
|
||||
[![Get it on F-Droid](https://i.imgur.com/HDicnzz.png)](https://f-droid.org/packages/nekox.messenger) Fdroid releases can not upgrade to other releases.
|
||||
|
||||
- [Update News Telegram](https://t.me/NekogramX)
|
||||
- [GitHub Feedback](https://github.com/NekoX-Dev/NekoX/issues)
|
||||
|
@ -19,63 +19,71 @@ We are no longer able to update versions on the Play Store, check https://telegr
|
|||
|
||||
- Most of Nekogram's features
|
||||
- Unlimited login accounts
|
||||
- OpenCC Chinese Convert
|
||||
- Built-in VMess, Shadowsocks, SSR, Trojan-GFW proxies support
|
||||
- Built-in public proxy list / Proxy subscription support
|
||||
- **Proxy**
|
||||
- Built-in VMess, Shadowsocks, SSR, Trojan-GFW proxies support (No longer maintained)
|
||||
- Built-in public proxy (WebSocket relay via Cloudflare CDN), [documentation and for PC](https://github.com/arm64v8a/NekoXProxy)
|
||||
- Proxy subscription support
|
||||
- Ipv6 MTProxy support
|
||||
- Able to parse all proxy subscription format: SIP008, ssr, v2rayN, vmess1, shit ios app formats, clash config and more
|
||||
- Proxies import and export, remarks, speed measurement, sorting, delete unusable nodes, etc
|
||||
- Scan the QR code (any link, can add a proxy)
|
||||
- The ( vmess / vmess1 / ss / ssr / trojan ) proxy link in the message can be clicked
|
||||
- Built-in WebSocket relay support (Connect via Cloudflare CDN)
|
||||
- Allow auto-disabling proxy when VPN is enabled
|
||||
- Proxy automatic switcher
|
||||
- Don't alert "Proxy unavailable" for non-current account
|
||||
- **Stickers**
|
||||
- Custom [Emoji packs](https://github.com/NekoX-Dev/NekoX/wiki/emoji)
|
||||
- Add stickers without sticker pack
|
||||
- Allow disabling vibration
|
||||
- Allow clicking on links in self profile
|
||||
- Sticker set list backup / restore / share
|
||||
- **Internationalization**
|
||||
- OpenCC Chinese Convert
|
||||
- Full InstantView translation support
|
||||
- Translation support for selected text on input and in messages
|
||||
- Delete all messages in group
|
||||
- Google Cloud Translate / Yandex.Translate support
|
||||
- Force English emoji keywords to be loaded
|
||||
- Persian calendar support
|
||||
- **Additional Options**
|
||||
- Option to disable vibration
|
||||
- Dialog sorting is optional "Unread and can be prioritized for reminding" etc
|
||||
- Allow to skip "regret within five seconds"
|
||||
- Unblock all users support
|
||||
- Login via QR code
|
||||
- Scan and confirm the login QR code directly
|
||||
- Allow clearing app data
|
||||
- Option to skip "regret within five seconds"
|
||||
- Option to not send comment first when forwarding
|
||||
- Option to use nekox chat input menu: replace record button with a menu which contains an switch to control link preview (enabled by default)
|
||||
- Option to disable link preview by default: to prevent the server from knowing that the link is shared through Telegram.
|
||||
- Option to ignore Android-only content restrictions (except for the Play Store version).
|
||||
- OpenKeychain client (sign / verify / decrypt / import)
|
||||
- Google Cloud Translate / Yandex.Translate support
|
||||
- Custom cache directory (supports external storage)
|
||||
- Custom app ID and Hash (optional NekoX / Android / Android X or Manual input)
|
||||
- Custom server (official, test DC)
|
||||
- Keep the original file name when downloading files
|
||||
- View the data center you belong to when you don't have an avatar
|
||||
- Proxies, groups, channels, sticker packs are able to shared as QR codes
|
||||
- Force English emoji keywords to be loaded
|
||||
- Add "@Name" when long-pressing @user option
|
||||
- Enhanced notification service, optional version without Google Services
|
||||
- Don't alert "Proxy unavailable" for non-current account
|
||||
- Option to block others from starting a secret chat with you
|
||||
- Allow creation of group without invite
|
||||
- Option to upgrade group to supergroup
|
||||
- Mark dialogs as read using tab menu
|
||||
- Improved session dialog
|
||||
- Improved link long click menu
|
||||
- Improved hide messages from blocked users feature
|
||||
- Text replacer
|
||||
- Persian calendar support
|
||||
- Option to disable trending
|
||||
- Telegram X style menu for unpinning messages
|
||||
- Built-in Material Design themes / Telegram X style icons
|
||||
- **Additional Actions**
|
||||
- Allow clicking on links in self profile
|
||||
- Delete all messages in group
|
||||
- Unblock all users support
|
||||
- Login via QR code
|
||||
- Scan and confirm the login QR code directly
|
||||
- Allow clearing app data
|
||||
- Proxies, groups, channels, sticker packs are able to be shared as QR codes
|
||||
- Add "@Name" when long-pressing @user option
|
||||
- Allow creating a group without inviting anyone
|
||||
- Allow upgrading a group to a supergroup
|
||||
- Mark dialogs as read using tab menu
|
||||
- Enabled set auto delete timer option for private chats and private groups
|
||||
- Don't process cleanup draft events after opening chat
|
||||
- Support saving multiple selected messages to Saved Messages
|
||||
- Support unpinning multiple selected messages
|
||||
- View stats option for messages
|
||||
- **Optimization**
|
||||
- Keep the original file name when downloading files
|
||||
- View the data center you belong to when you don't have an avatar
|
||||
- Enhanced notification service, optional version without Google Services
|
||||
- Improved session dialog
|
||||
- Improved link long click menu
|
||||
- Improved hide messages from blocked users feature
|
||||
- Don't process cleanup draft events after opening chat
|
||||
- **Others**
|
||||
- OpenKeychain client (sign / verify / decrypt / import)
|
||||
- Text replacer
|
||||
- **UI**
|
||||
- Telegram X style menu for unpinning messages
|
||||
- Built-in Material Design themes / Telegram X style icons
|
||||
- And more :)
|
||||
|
||||
## Compilation Guide
|
||||
|
@ -85,14 +93,18 @@ Consider using a Linux VM or dual booting.**
|
|||
|
||||
**Important:**
|
||||
|
||||
0. Checkout all submodules
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
1. Install Android SDK and NDK (default location is $HOME/Android/SDK, otherwise you need to specify $ANDROID_HOME for it)
|
||||
|
||||
It is recommended to use [AndroidStudio](https://developer.android.com/studio) to install.
|
||||
|
||||
2. Install golang ( 1.16 ).
|
||||
2. Install golang and yasm
|
||||
```shell
|
||||
# debian sid
|
||||
apt install -y golang-1.16
|
||||
apt install -y golang-1.16 yasm
|
||||
```
|
||||
|
||||
3. Install Rust and its stdlib for Android ABIs, and add environment variables for it.
|
||||
|
|
|
@ -3,15 +3,15 @@ import cn.hutool.core.util.RuntimeUtil
|
|||
apply plugin: "com.android.application"
|
||||
apply plugin: "kotlin-android"
|
||||
|
||||
def verName = "7.9.3-preview01"
|
||||
def verCode = 370
|
||||
def verName = "9.5.6-preview01"
|
||||
def verCode = 725
|
||||
|
||||
if (System.getenv("DEBUG_BUILD") == "true") {
|
||||
verName += "-" + RuntimeUtil.execForStr("git log --pretty=format:'%h' -n 1")
|
||||
verName += "-" + RuntimeUtil.execForStr("git log --pretty=format:'%h' -n 1").trim().replace('\'','')
|
||||
}
|
||||
|
||||
def officialVer = "7.9.3"
|
||||
def officialCode = 2390
|
||||
def officialVer = "9.5.6"
|
||||
def officialCode = 3237
|
||||
|
||||
def serviceAccountCredentialsFile = rootProject.file("service_account_credentials.json")
|
||||
|
||||
|
@ -39,6 +39,7 @@ configurations {
|
|||
def keystorePwd = null
|
||||
def alias = null
|
||||
def pwd = null
|
||||
def disableCMakeRelWithDebInfo = System.getenv("COMPILE_NATIVE") == null
|
||||
|
||||
Properties properties
|
||||
def base64 = System.getenv("LOCAL_PROPERTIES")
|
||||
|
@ -60,17 +61,10 @@ keystorePwd = keystorePwd ?: System.getenv("KEYSTORE_PASS")
|
|||
alias = alias ?: System.getenv("ALIAS_NAME")
|
||||
pwd = pwd ?: System.getenv("ALIAS_PASS")
|
||||
|
||||
def targetAbi = ""
|
||||
def targetTask = ""
|
||||
if (!gradle.startParameter.taskNames.isEmpty()) {
|
||||
if (gradle.startParameter.taskNames.size == 1) {
|
||||
targetTask = gradle.startParameter.taskNames[0].toLowerCase()
|
||||
if (targetTask.contains("arm64")) {
|
||||
targetAbi = "arm64"
|
||||
} else if (targetTask.contains("arm")) {
|
||||
targetAbi = "arm"
|
||||
verCode -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,8 +72,8 @@ def nativeTarget = System.getenv("NATIVE_TARGET")
|
|||
if (nativeTarget == null) nativeTarget = ""
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion "31.0.0"
|
||||
compileSdkVersion 33
|
||||
buildToolsVersion "33.0.0"
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
defaultConfig.applicationId = "nekox.messenger"
|
||||
|
@ -87,29 +81,31 @@ android {
|
|||
splits {
|
||||
|
||||
abi {
|
||||
|
||||
if (targetTask.contains("fdroid")) {
|
||||
enable false
|
||||
universalApk true
|
||||
} else {
|
||||
if (nativeTarget.toLowerCase().equals("universal")) {
|
||||
enable false
|
||||
universalApk true
|
||||
} else if (!nativeTarget.isBlank()) {
|
||||
enable true
|
||||
universalApk false
|
||||
|
||||
if (!targetAbi.isBlank()) {
|
||||
reset()
|
||||
if (targetAbi == "arm64") {
|
||||
include "arm64-v8a"
|
||||
} else if (targetAbi == "arm") {
|
||||
include "armeabi-v7a"
|
||||
}
|
||||
} else if (!nativeTarget.isBlank()) {
|
||||
reset()
|
||||
include nativeTarget
|
||||
} else {
|
||||
enable true
|
||||
universalApk false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
minSdkVersion 19
|
||||
//noinspection ExpiredTargetSdkVersion,OldTargetApi
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 32
|
||||
|
||||
versionName verName
|
||||
versionCode verCode
|
||||
|
@ -133,7 +129,7 @@ android {
|
|||
externalNativeBuild {
|
||||
cmake {
|
||||
version "3.10.2"
|
||||
arguments "-DANDROID_STL=c++_static", "-DANDROID_PLATFORM=android-16", "-j=${Runtime.getRuntime().availableProcessors()}"
|
||||
arguments "-DANDROID_STL=c++_static", "-DANDROID_PLATFORM=android-16"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,38 +147,20 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable "MissingTranslation"
|
||||
disable "ExtraTranslation"
|
||||
disable "BlockedPrivateApi"
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
|
||||
exclude "/fabric/**"
|
||||
exclude "/META-INF/*.version"
|
||||
exclude "/META-INF/*.kotlin_module"
|
||||
exclude "/META-INF/native-image/**"
|
||||
exclude "/builddef.lst"
|
||||
exclude "/*.txt"
|
||||
exclude "/DebugProbesKt.bin"
|
||||
exclude "/okhttp3/internal/publicsuffix/NOTICE"
|
||||
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
jumboMode = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
|
||||
testOptions {
|
||||
|
@ -244,12 +222,7 @@ android {
|
|||
matchingFallbacks = ["release", "debug"]
|
||||
}
|
||||
|
||||
fdroidArmRelease {
|
||||
initWith foss
|
||||
matchingFallbacks = ["release", "debug"]
|
||||
}
|
||||
|
||||
fdroidArm64Release {
|
||||
fdroidRelease {
|
||||
initWith foss
|
||||
matchingFallbacks = ["release", "debug"]
|
||||
}
|
||||
|
@ -259,24 +232,21 @@ android {
|
|||
|
||||
main {
|
||||
jni.srcDirs = []
|
||||
assets.srcDirs = ["src/main/assets", "src/emojis/twitter"]
|
||||
}
|
||||
|
||||
debug {
|
||||
java {
|
||||
srcDirs "src/main/java", "src/gservcies/java"
|
||||
}
|
||||
jni {
|
||||
srcDirs = ["./jni/"]
|
||||
}
|
||||
jniLibs {
|
||||
srcDirs = []
|
||||
}
|
||||
jni.srcDirs = ["./jni/"]
|
||||
manifest {
|
||||
srcFile "src/gservcies/AndroidManifest.xml"
|
||||
}
|
||||
}
|
||||
|
||||
releaseNoGcm {
|
||||
jni.srcDirs = []
|
||||
jniLibs {
|
||||
srcDir "src/main/libs"
|
||||
}
|
||||
|
@ -286,6 +256,7 @@ android {
|
|||
java {
|
||||
srcDirs "src/main/java", "src/gservcies/java"
|
||||
}
|
||||
jni.srcDirs = []
|
||||
jniLibs {
|
||||
srcDir "src/main/libs"
|
||||
}
|
||||
|
@ -300,22 +271,11 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
fdroidArmRelease {
|
||||
fdroidRelease {
|
||||
jni {
|
||||
srcDirs = ["./jni/"]
|
||||
}
|
||||
jniLibs {
|
||||
srcDirs = []
|
||||
}
|
||||
}
|
||||
|
||||
fdroidArm64Release {
|
||||
jni {
|
||||
srcDirs = ["./jni/"]
|
||||
}
|
||||
jniLibs {
|
||||
srcDirs = []
|
||||
}
|
||||
jniLibs.srcDirs = []
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -325,16 +285,15 @@ android {
|
|||
productFlavors {
|
||||
mini {
|
||||
isDefault true
|
||||
}
|
||||
miniAppleEmoji {
|
||||
manifestPlaceholders = [is_full_version: "false"]
|
||||
}
|
||||
full {
|
||||
}
|
||||
fullAppleEmoji {
|
||||
manifestPlaceholders = [is_full_version: "true"]
|
||||
}
|
||||
miniPlay {
|
||||
versionNameSuffix "-play"
|
||||
versionCode verCode - 2
|
||||
manifestPlaceholders = [is_full_version: "false"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,15 +303,6 @@ android {
|
|||
implementation fileTree("libs")
|
||||
}
|
||||
}
|
||||
if (set.name.matches("(mini|full).*")) {
|
||||
if (set.name.contains("Apple")) {
|
||||
set.assets.srcDirs = ["src/main/assets", "src/emojis/apple"]
|
||||
/*} else if (set.name.contains("Twitter")) {
|
||||
set.assets.srcDirs = ["src/main/assets", "src/emojis/twitter"]*/
|
||||
} else {
|
||||
set.assets.srcDirs = ["src/main/assets", "src/emojis/twitter"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.all { task ->
|
||||
|
@ -362,63 +312,58 @@ android {
|
|||
if (task.name.contains("uploadCrashlyticsMappingFile")) {
|
||||
enabled = false
|
||||
}
|
||||
if (disableCMakeRelWithDebInfo && task.name.contains("CMakeRelWithDebInfo") && !targetTask.contains("fdroid")) {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
def flavor = variant.mergedFlavor
|
||||
if (variant.buildType.name == "fdroidArmRelease") {
|
||||
variant.outputs.each { output ->
|
||||
output.versionNameOverride = flavor.versionName + "-arm"
|
||||
}
|
||||
} else if (variant.buildType.name == "fdroidArm64Release") {
|
||||
variant.outputs.each { output ->
|
||||
output.versionNameOverride = flavor.versionName + "-arm64"
|
||||
}
|
||||
}
|
||||
variant.outputs.all { output ->
|
||||
outputFileName = outputFileName.replace("TMessagesProj", "NekoX")
|
||||
outputFileName = outputFileName.replace("TMessagesProj", "NekoX-v" + versionName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def okHttpVersion = "5.0.0-alpha.2"
|
||||
def fcmVersion = "21.1.0"
|
||||
def crashlyticsVersion = "18.2.0"
|
||||
def playCoreVersion = "1.10.0"
|
||||
def fcmVersion = "23.0.7"
|
||||
def crashlyticsVersion = "18.2.12"
|
||||
def playCoreVersion = "1.10.3"
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation "androidx.browser:browser:1.3.0"
|
||||
implementation "androidx.core:core-ktx:1.6.0"
|
||||
implementation "androidx.browser:browser:1.5.0"
|
||||
implementation "androidx.core:core-ktx:1.9.0"
|
||||
implementation "androidx.palette:palette-ktx:1.0.0"
|
||||
implementation "androidx.viewpager:viewpager:1.0.0"
|
||||
implementation "androidx.exifinterface:exifinterface:1.3.3"
|
||||
implementation "androidx.exifinterface:exifinterface:1.3.6"
|
||||
implementation "androidx.interpolator:interpolator:1.0.0"
|
||||
implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
implementation "androidx.sharetarget:sharetarget:1.1.0"
|
||||
implementation "androidx.sharetarget:sharetarget:1.2.0"
|
||||
|
||||
compileOnly "org.checkerframework:checker-qual:3.16.0"
|
||||
compileOnly "org.checkerframework:checker-compat-qual:2.5.5"
|
||||
// just follow official
|
||||
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
||||
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
||||
|
||||
// don"t change this :)
|
||||
//noinspection GradleDependency
|
||||
implementation "com.googlecode.mp4parser:isoparser:1.0.6"
|
||||
|
||||
implementation "com.google.code.gson:gson:2.8.7"
|
||||
implementation "com.google.code.gson:gson:2.8.9"
|
||||
implementation "org.osmdroid:osmdroid-android:6.1.10"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.20"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1-native-mt"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
|
||||
|
||||
implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
|
||||
implementation 'dnsjava:dnsjava:3.4.0'
|
||||
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10"
|
||||
implementation 'com.neovisionaries:nv-websocket-client:2.14'
|
||||
implementation 'dnsjava:dnsjava:3.4.1'
|
||||
implementation "org.dizitart:nitrite:3.4.3"
|
||||
|
||||
implementation "cn.hutool:hutool-core:5.7.5"
|
||||
implementation "cn.hutool:hutool-crypto:5.7.5"
|
||||
implementation "cn.hutool:hutool-core:5.7.13"
|
||||
implementation "cn.hutool:hutool-crypto:5.7.13"
|
||||
implementation 'cn.hutool:hutool-http:5.7.5'
|
||||
implementation "com.jakewharton:process-phoenix:2.0.0"
|
||||
implementation "com.jakewharton:process-phoenix:2.1.2"
|
||||
implementation 'com.google.guava:guava:31.1-android'
|
||||
|
||||
compileOnly 'org.yaml:snakeyaml:1.29'
|
||||
fullImplementation 'org.yaml:snakeyaml:1.29'
|
||||
|
@ -431,6 +376,11 @@ dependencies {
|
|||
compileOnly "com.google.firebase:firebase-crashlytics:$crashlyticsVersion"
|
||||
compileOnly "com.google.android.play:core:$playCoreVersion"
|
||||
|
||||
debugImplementation 'com.google.android.gms:play-services-maps:18.1.0'
|
||||
debugImplementation 'com.google.android.gms:play-services-location:20.0.0'
|
||||
releaseImplementation 'com.google.android.gms:play-services-maps:18.1.0'
|
||||
releaseImplementation 'com.google.android.gms:play-services-location:20.0.0'
|
||||
|
||||
debugImplementation "com.google.firebase:firebase-messaging:$fcmVersion"
|
||||
debugImplementation "com.google.firebase:firebase-crashlytics:$crashlyticsVersion"
|
||||
debugImplementation "com.google.android.play:core:$playCoreVersion"
|
||||
|
@ -439,10 +389,10 @@ dependencies {
|
|||
releaseImplementation "com.google.android.play:core:$playCoreVersion"
|
||||
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
testImplementation "androidx.test:core:1.4.0"
|
||||
testImplementation "androidx.test:core:1.5.0"
|
||||
testImplementation "org.robolectric:robolectric:4.5.1"
|
||||
|
||||
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
|
||||
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.2.0"
|
||||
|
||||
}
|
||||
|
||||
|
@ -451,6 +401,20 @@ apply plugin: "com.google.firebase.crashlytics"
|
|||
|
||||
android {
|
||||
|
||||
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
excludes += ['/fabric/**', '/META-INF/native-image/**']
|
||||
}
|
||||
resources {
|
||||
excludes += ['/fabric/**', '/META-INF/*.version', '/META-INF/*.kotlin_module', '/META-INF/native-image/**', '/builddef.lst', '/*.txt', '/DebugProbesKt.bin', '/okhttp3/internal/publicsuffix/NOTICE']
|
||||
}
|
||||
}
|
||||
namespace "org.telegram.messenger"
|
||||
lint {
|
||||
disable 'MissingTranslation', 'ExtraTranslation', 'BlockedPrivateApi'
|
||||
}
|
||||
|
||||
tasks.all { task ->
|
||||
if (task.name.startsWith("uploadCrashlyticsMappingFile")) {
|
||||
task.enabled = false
|
||||
|
|
|
@ -5,16 +5,26 @@ set(CMAKE_C_FLAGS "-w -std=c11 -DANDROID -D_LARGEFILE_SOURCE=1")
|
|||
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,libtgvoip.a,libtgcalls.a,libtgcalls_tp.a,libtgnet.a,liblz4.a,libwebp.a,libflac.a,librlottie.a,libsqlite.a,
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs,libtgvoip.a,libtgcalls.a,libtgcalls_tp.a,libtgnet.a,liblz4.a,libwebp.a,libflac.a,librlottie.a,libsqlite.a,
|
||||
${CMAKE_HOME_DIRECTORY}/ffmpeg/build/${ANDROID_ABI}/lib/libswscale.a,
|
||||
${CMAKE_HOME_DIRECTORY}/ffmpeg/build/${ANDROID_ABI}/lib/libavformat.a,
|
||||
${CMAKE_HOME_DIRECTORY}/ffmpeg/build/${ANDROID_ABI}/lib/libavcodec.a,
|
||||
${CMAKE_HOME_DIRECTORY}/ffmpeg/build/${ANDROID_ABI}/lib/libavresample.a,
|
||||
${CMAKE_HOME_DIRECTORY}/ffmpeg/build/${ANDROID_ABI}/lib/libavutil.a,
|
||||
${CMAKE_HOME_DIRECTORY}/ffmpeg/build/${ANDROID_ABI}/lib/libswresample.a,
|
||||
${CMAKE_HOME_DIRECTORY}/libvpx/build/${ANDROID_ABI}/lib/libvpx.a,
|
||||
${CMAKE_HOME_DIRECTORY}/boringssl/build/${ANDROID_ABI}/ssl/libssl.a,
|
||||
${CMAKE_HOME_DIRECTORY}/boringssl/build/${ANDROID_ABI}/crypto/libcrypto.a")
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Os -DNDEBUG")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Os -DNDEBUG")
|
||||
set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-exclude-libs,ALL -Wl,--gc-sections -Wl,--strip-all")
|
||||
endif()
|
||||
|
||||
if (${ANDROID_ABI} STREQUAL "armeabi-v7a" OR ${ANDROID_ABI} STREQUAL "arm64-v8a")
|
||||
enable_language(ASM)
|
||||
else()
|
||||
|
@ -45,6 +55,10 @@ set_target_properties(crypto PROPERTIES IMPORTED_LOCATION ${CMAKE_HOME_DIRECTORY
|
|||
add_library(ssl STATIC IMPORTED)
|
||||
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION ${CMAKE_HOME_DIRECTORY}/boringssl/build/${ANDROID_ABI}/ssl/libssl.a)
|
||||
|
||||
add_library(libvpx STATIC IMPORTED)
|
||||
set_target_properties(libvpx PROPERTIES IMPORTED_LOCATION ${CMAKE_HOME_DIRECTORY}/libvpx/build/${ANDROID_ABI}/lib/libvpx.a)
|
||||
|
||||
|
||||
#tgnet
|
||||
add_library(mozjpeg STATIC
|
||||
mozjpeg/cjpeg.c mozjpeg/cdjpeg.c mozjpeg/rdgif.c mozjpeg/rdppm.c mozjpeg/rdjpeg.c mozjpeg/rdswitch.c mozjpeg/rdbmp.c
|
||||
|
@ -445,10 +459,41 @@ set_target_properties(sqlite PROPERTIES
|
|||
target_compile_definitions(sqlite PUBLIC
|
||||
NULL=0 SOCKLEN_T=socklen_t LOCALE_NOT_USED ANDROID_NDK DISABLE_IMPORTGL AVOID_TABLES ANDROID_TILE_BASED_DECODE HAVE_STRCHRNUL=0 ANDROID_ARMV6_IDCT)
|
||||
|
||||
#breakpad
|
||||
add_library(breakpad STATIC
|
||||
third_party/breakpad/src/client/linux/crash_generation/crash_generation_client.cc
|
||||
third_party/breakpad/src/client/linux/handler/exception_handler.cc
|
||||
third_party/breakpad/src/client/linux/handler/minidump_descriptor.cc
|
||||
third_party/breakpad/src/client/linux/log/log.cc
|
||||
third_party/breakpad/src/client/linux/dump_writer_common/thread_info.cc
|
||||
third_party/breakpad/src/client/linux/dump_writer_common/seccomp_unwinder.cc
|
||||
third_party/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc
|
||||
third_party/breakpad/src/client/linux/microdump_writer/microdump_writer.cc
|
||||
third_party/breakpad/src/client/linux/minidump_writer/linux_dumper.cc
|
||||
third_party/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
|
||||
third_party/breakpad/src/client/linux/minidump_writer/minidump_writer.cc
|
||||
third_party/breakpad/src/client/minidump_file_writer.cc
|
||||
third_party/breakpad/src/common/android/breakpad_getcontext.S
|
||||
third_party/breakpad/src/common/convert_UTF.c
|
||||
third_party/breakpad/src/common/md5.cc
|
||||
third_party/breakpad/src/common/string_conversion.cc
|
||||
third_party/breakpad/src/common/linux/elfutils.cc
|
||||
third_party/breakpad/src/common/linux/file_id.cc
|
||||
third_party/breakpad/src/common/linux/guid_creator.cc
|
||||
third_party/breakpad/src/common/linux/linux_libc_support.cc
|
||||
third_party/breakpad/src/common/linux/memory_mapped_file.cc
|
||||
third_party/breakpad/src/common/linux/safe_readlink.cc)
|
||||
set_target_properties(breakpad PROPERTIES ANDROID_ARM_MODE arm)
|
||||
set_property(SOURCE third_party/breakpad/src/common/android/breakpad_getcontext.S PROPERTY LANGUAGE C)
|
||||
target_include_directories(breakpad PUBLIC
|
||||
third_party/breakpad/src/common/android/include
|
||||
third_party/breakpad/src)
|
||||
|
||||
|
||||
#voip
|
||||
include(${CMAKE_HOME_DIRECTORY}/voip/CMakeLists.txt)
|
||||
|
||||
set(NATIVE_LIB "tmessages.40")
|
||||
set(NATIVE_LIB "tmessages.43")
|
||||
|
||||
#tmessages
|
||||
add_library(${NATIVE_LIB} SHARED
|
||||
|
@ -696,7 +741,12 @@ target_sources(${NATIVE_LIB} PRIVATE
|
|||
third_party/libyuv/source/scale_win.cc
|
||||
third_party/libyuv/source/scale.cc
|
||||
third_party/libyuv/source/video_common.cc
|
||||
third_party/libyuv/source/scale_uv.cc)
|
||||
third_party/libyuv/source/scale_uv.cc
|
||||
third_party/libyuv/source/rotate_lsx.cc
|
||||
third_party/libyuv/source/row_lasx.cc
|
||||
third_party/libyuv/source/row_lsx.cc
|
||||
third_party/libyuv/source/scale_lsx.cc
|
||||
third_party/libyuv/source/scale_rgb.cc)
|
||||
|
||||
target_include_directories(${NATIVE_LIB} PUBLIC
|
||||
opus/include
|
||||
|
@ -708,6 +758,7 @@ target_include_directories(${NATIVE_LIB} PUBLIC
|
|||
third_party/libyuv/include
|
||||
boringssl/include
|
||||
ffmpeg/build/${ANDROID_ABI}/include
|
||||
libvpx/build/${ANDROID_ABI}/include
|
||||
emoji
|
||||
exoplayer/include
|
||||
exoplayer/libFLAC/include
|
||||
|
@ -734,6 +785,7 @@ target_link_libraries(${NATIVE_LIB}
|
|||
avcodec
|
||||
avresample
|
||||
swresample
|
||||
libvpx
|
||||
avutil
|
||||
ssl
|
||||
crypto
|
||||
|
@ -744,18 +796,9 @@ target_link_libraries(${NATIVE_LIB}
|
|||
EGL
|
||||
android
|
||||
OpenSLES
|
||||
cpufeatures)
|
||||
cpufeatures
|
||||
breakpad)
|
||||
|
||||
if (${ANDROID_ABI} STREQUAL "x86" OR ${ANDROID_ABI} STREQUAL "x86_64")
|
||||
target_link_libraries(${NATIVE_LIB}
|
||||
-Wl,--whole-archive libvpx_yasm -Wl,--no-whole-archive)
|
||||
endif ()
|
||||
|
||||
#if (${ANDROID_ABI} STREQUAL "x86" OR ${ANDROID_ABI} STREQUAL "x86_64")
|
||||
# target_link_libraries(${NATIVE_LIB}
|
||||
# -Wl,--whole-archive vpxasm -Wl,--no-whole-archive
|
||||
# c)
|
||||
#endif()
|
||||
|
||||
include(AndroidNdkModules)
|
||||
android_ndk_import_module_cpufeatures()
|
|
@ -5,6 +5,7 @@
|
|||
#include "tgnet/ConnectionsManager.h"
|
||||
#include "tgnet/MTProtoScheme.h"
|
||||
#include "tgnet/ConnectionSocket.h"
|
||||
#include "tgnet/FileLog.h"
|
||||
|
||||
JavaVM *java;
|
||||
jclass jclass_RequestDelegateInternal;
|
||||
|
@ -99,16 +100,19 @@ void sendRequest(JNIEnv *env, jclass c, jint instanceNum, jlong object, jobject
|
|||
TL_api_request *request = new TL_api_request();
|
||||
request->request = (NativeByteBuffer *) (intptr_t) object;
|
||||
if (onComplete != nullptr) {
|
||||
DEBUG_REF("sendRequest onComplete");
|
||||
onComplete = env->NewGlobalRef(onComplete);
|
||||
}
|
||||
if (onQuickAck != nullptr) {
|
||||
DEBUG_REF("sendRequest onQuickAck");
|
||||
onQuickAck = env->NewGlobalRef(onQuickAck);
|
||||
}
|
||||
if (onWriteToSocket != nullptr) {
|
||||
DEBUG_REF("sendRequest onWriteToSocket");
|
||||
onWriteToSocket = env->NewGlobalRef(onWriteToSocket);
|
||||
}
|
||||
ConnectionsManager::getInstance(instanceNum).sendRequest(request, ([onComplete, instanceNum](
|
||||
TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
TL_api_response *resp = (TL_api_response *) response;
|
||||
jlong ptr = 0;
|
||||
jint errorCode = 0;
|
||||
|
@ -209,7 +213,7 @@ jint getConnectionState(JNIEnv *env, jclass c, jint instanceNum) {
|
|||
return ConnectionsManager::getInstance(instanceNum).getConnectionState();
|
||||
}
|
||||
|
||||
void setUserId(JNIEnv *env, jclass c, jint instanceNum, int32_t id) {
|
||||
void setUserId(JNIEnv *env, jclass c, jint instanceNum, int64_t id) {
|
||||
ConnectionsManager::getInstance(instanceNum).setUserId(id);
|
||||
}
|
||||
|
||||
|
@ -262,6 +266,7 @@ checkProxy(JNIEnv *env, jclass c, jint instanceNum, jstring address, jint port,
|
|||
const char *secretStr = env->GetStringUTFChars(secret, 0);
|
||||
|
||||
if (requestTimeFunc != nullptr) {
|
||||
DEBUG_REF("sendRequest requestTimeFunc");
|
||||
requestTimeFunc = env->NewGlobalRef(requestTimeFunc);
|
||||
}
|
||||
|
||||
|
@ -320,7 +325,7 @@ class Delegate : public ConnectiosManagerDelegate {
|
|||
if (connectionType == ConnectionTypeGeneric) {
|
||||
jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager,
|
||||
jclass_ConnectionsManager_onUnparsedMessageReceived,
|
||||
(jlong) (intptr_t) buffer, instanceNum);
|
||||
(jlong) (intptr_t) buffer, instanceNum, reqMessageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,8 +438,8 @@ void setSystemLangCode(JNIEnv *env, jclass c, jint instanceNum, jstring langCode
|
|||
void init(JNIEnv *env, jclass c, jint instanceNum, jint version, jint layer, jint apiId,
|
||||
jstring deviceModel, jstring systemVersion, jstring appVersion, jstring langCode,
|
||||
jstring systemLangCode, jstring configPath, jstring logPath, jstring regId,
|
||||
jstring cFingerprint, jstring installerId, jstring packageId, jint timezoneOffset, jint userId,
|
||||
jboolean enablePushConnection, jboolean hasNetwork, jint networkType) {
|
||||
jstring cFingerprint, jstring installerId, jstring packageId, jint timezoneOffset, jlong userId,
|
||||
jboolean enablePushConnection, jboolean hasNetwork, jint networkType, jint performanceClass) {
|
||||
const char *deviceModelStr = env->GetStringUTFChars(deviceModel, 0);
|
||||
const char *systemVersionStr = env->GetStringUTFChars(systemVersion, 0);
|
||||
const char *appVersionStr = env->GetStringUTFChars(appVersion, 0);
|
||||
|
@ -459,7 +464,7 @@ void init(JNIEnv *env, jclass c, jint instanceNum, jint version, jint layer, jin
|
|||
std::string(cFingerprintStr),
|
||||
std::string(installerIdStr), std::string(packageIdStr), timezoneOffset,
|
||||
userId, true, enablePushConnection,
|
||||
hasNetwork, networkType);
|
||||
hasNetwork, networkType, performanceClass);
|
||||
|
||||
if (deviceModelStr != 0) {
|
||||
env->ReleaseStringUTFChars(deviceModel, deviceModelStr);
|
||||
|
@ -523,8 +528,8 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
|
|||
{"native_moveToDatacenter", "(II)V", (void *) moveToDatacenter},
|
||||
{"native_setProxySettings", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void *) setProxySettings},
|
||||
{"native_getConnectionState", "(I)I", (void *) getConnectionState},
|
||||
{"native_setUserId", "(II)V", (void *) setUserId},
|
||||
{"native_init", "(IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIZZI)V", (void *) init},
|
||||
{"native_setUserId", "(IJ)V", (void *) setUserId},
|
||||
{"native_init", "(IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJZZII)V", (void *) init},
|
||||
{"native_setLangCode", "(ILjava/lang/String;)V", (void *) setLangCode},
|
||||
{"native_setRegId", "(ILjava/lang/String;)V", (void *) setRegId},
|
||||
{"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode},
|
||||
|
@ -569,18 +574,19 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
|||
sizeof(ConnectionsManagerMethods[0]))) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
DEBUG_REF("RequestDelegateInternal class");
|
||||
jclass_RequestDelegateInternal = (jclass) env->NewGlobalRef(
|
||||
env->FindClass("org/telegram/tgnet/RequestDelegateInternal"));
|
||||
if (jclass_RequestDelegateInternal == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
jclass_RequestDelegateInternal_run = env->GetMethodID(jclass_RequestDelegateInternal, "run",
|
||||
"(JILjava/lang/String;IJ)V");
|
||||
"(JILjava/lang/String;IJJ)V");
|
||||
if (jclass_RequestDelegateInternal_run == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
DEBUG_REF("RequestTimeDelegate class");
|
||||
jclass_RequestTimeDelegate = (jclass) env->NewGlobalRef(
|
||||
env->FindClass("org/telegram/tgnet/RequestTimeDelegate"));
|
||||
if (jclass_RequestTimeDelegate == 0) {
|
||||
|
@ -591,6 +597,7 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
|||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
DEBUG_REF("QuickAckDelegate class");
|
||||
jclass_QuickAckDelegate = (jclass) env->NewGlobalRef(
|
||||
env->FindClass("org/telegram/tgnet/QuickAckDelegate"));
|
||||
if (jclass_RequestDelegateInternal == 0) {
|
||||
|
@ -601,6 +608,7 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
|||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
DEBUG_REF("WriteToSocketDelegate class");
|
||||
jclass_WriteToSocketDelegate = (jclass) env->NewGlobalRef(
|
||||
env->FindClass("org/telegram/tgnet/WriteToSocketDelegate"));
|
||||
if (jclass_WriteToSocketDelegate == 0) {
|
||||
|
@ -610,13 +618,14 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) {
|
|||
if (jclass_WriteToSocketDelegate_run == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
DEBUG_REF("ConnectionsManager class");
|
||||
jclass_ConnectionsManager = (jclass) env->NewGlobalRef(
|
||||
env->FindClass("org/telegram/tgnet/ConnectionsManager"));
|
||||
if (jclass_ConnectionsManager == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
jclass_ConnectionsManager_onUnparsedMessageReceived = env->GetStaticMethodID(
|
||||
jclass_ConnectionsManager, "onUnparsedMessageReceived", "(JI)V");
|
||||
jclass_ConnectionsManager, "onUnparsedMessageReceived", "(JIJ)V");
|
||||
if (jclass_ConnectionsManager_onUnparsedMessageReceived == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
|
|
@ -219,7 +219,7 @@ static int writeOggPage(ogg_page *page, FILE *os) {
|
|||
return written;
|
||||
}
|
||||
|
||||
const opus_int32 bitrate = 30 * 1024;
|
||||
const opus_int32 bitrate = OPUS_BITRATE_MAX;
|
||||
const opus_int32 frame_size = 960;
|
||||
const int with_cvbr = 1;
|
||||
const int max_ogg_delay = 0;
|
||||
|
@ -290,11 +290,13 @@ int initRecorder(const char *path, opus_int32 sampleRate) {
|
|||
rate = sampleRate;
|
||||
|
||||
if (!path) {
|
||||
LOGE("path is null");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_fileOs = fopen(path, "wb");
|
||||
_fileOs = fopen(path, "w");
|
||||
if (!_fileOs) {
|
||||
LOGE("error cannot open file: %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -322,7 +324,7 @@ int initRecorder(const char *path, opus_int32 sampleRate) {
|
|||
header.nb_streams = 1;
|
||||
|
||||
int result = OPUS_OK;
|
||||
_encoder = opus_encoder_create(coding_rate, 1, OPUS_APPLICATION_AUDIO, &result);
|
||||
_encoder = opus_encoder_create(coding_rate, 1, OPUS_APPLICATION_VOIP, &result);
|
||||
if (result != OPUS_OK) {
|
||||
LOGE("Error cannot create encoder: %s", opus_strerror(result));
|
||||
return 0;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 04b3213d43492b6c9e0434d8e2a4530a9938f958
|
||||
Subproject commit 9bcc12d540c3b844ba317f042c731d64142af725
|
|
@ -7,7 +7,14 @@ function build_one {
|
|||
cd ${CPU}
|
||||
|
||||
echo "Configuring..."
|
||||
cmake -DANDROID_NATIVE_API_LEVEL=${API} -DANDROID_ABI=${CPU} -DCMAKE_BUILD_TYPE=Release -DANDROID_NDK=${NDK} -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=16 -GNinja -DCMAKE_MAKE_PROGRAM=${NINJA_PATH} ../..
|
||||
cmake \
|
||||
-DANDROID_NATIVE_API_LEVEL=${API} \
|
||||
-DANDROID_ABI=${CPU} \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DANDROID_NDK=${NDK} \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${NDK}/build/cmake/android.toolchain.cmake \
|
||||
-GNinja -DCMAKE_MAKE_PROGRAM=${NINJA_PATH} \
|
||||
../..
|
||||
|
||||
echo "Building..."
|
||||
cmake --build .
|
||||
|
@ -39,20 +46,41 @@ checkPreRequisites
|
|||
|
||||
cd boringssl
|
||||
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
API=16
|
||||
|
||||
CPU=armeabi-v7a
|
||||
function build {
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
x86_64)
|
||||
API=21
|
||||
CPU=x86_64
|
||||
build_one
|
||||
|
||||
CPU=x86
|
||||
build_one
|
||||
|
||||
;;
|
||||
arm64)
|
||||
API=21
|
||||
CPU=arm64-v8a
|
||||
build_one
|
||||
|
||||
CPU=x86_64
|
||||
;;
|
||||
arm)
|
||||
API=16
|
||||
CPU=armeabi-v7a
|
||||
build_one
|
||||
;;
|
||||
x86)
|
||||
API=16
|
||||
CPU=x86
|
||||
build_one
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
if (( $# == 0 )); then
|
||||
build x86_64 arm64 arm x86
|
||||
else
|
||||
build $@
|
||||
fi
|
||||
|
|
|
@ -1,185 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
function build_one {
|
||||
|
||||
echo "Cleaning..."
|
||||
rm config.h
|
||||
make clean
|
||||
|
||||
echo "Configuring..."
|
||||
|
||||
./configure \
|
||||
--cc=$CC \
|
||||
--nm=$NM \
|
||||
--enable-stripping \
|
||||
--arch=$ARCH \
|
||||
--target-os=linux \
|
||||
--enable-cross-compile \
|
||||
--x86asmexe=$NDK/prebuilt/$BUILD_PLATFORM/bin/yasm \
|
||||
--prefix=$PREFIX \
|
||||
--enable-pic \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
--enable-asm \
|
||||
--enable-inline-asm \
|
||||
--cross-prefix=$CROSS_PREFIX \
|
||||
--sysroot=$PLATFORM \
|
||||
--extra-cflags="-Wl,-Bsymbolic -Os -DCONFIG_LINUX_PERF=0 -DANDROID $OPTIMIZE_CFLAGS -fPIE -pie --static -fPIC" \
|
||||
--extra-ldflags="-Wl,-Bsymbolic -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lc -lm -ldl -fPIC" \
|
||||
--extra-libs="-lgcc" \
|
||||
\
|
||||
--enable-version3 \
|
||||
--enable-gpl \
|
||||
\
|
||||
--disable-linux-perf \
|
||||
\
|
||||
--disable-doc \
|
||||
--disable-htmlpages \
|
||||
--disable-avx \
|
||||
\
|
||||
--disable-everything \
|
||||
--disable-network \
|
||||
--disable-zlib \
|
||||
--disable-avfilter \
|
||||
--disable-avdevice \
|
||||
--disable-postproc \
|
||||
--disable-debug \
|
||||
--disable-programs \
|
||||
--disable-network \
|
||||
--disable-ffplay \
|
||||
--disable-ffprobe \
|
||||
--disable-postproc \
|
||||
--disable-avdevice \
|
||||
\
|
||||
--enable-runtime-cpudetect \
|
||||
--enable-pthreads \
|
||||
--enable-avresample \
|
||||
--enable-swscale \
|
||||
--enable-protocol=file \
|
||||
--enable-decoder=h264 \
|
||||
--enable-decoder=mpeg4 \
|
||||
--enable-decoder=mjpeg \
|
||||
--enable-decoder=gif \
|
||||
--enable-decoder=alac \
|
||||
--enable-demuxer=mov \
|
||||
--enable-demuxer=gif \
|
||||
--enable-hwaccels \
|
||||
--enable-runtime-cpudetect \
|
||||
$ADDITIONAL_CONFIGURE_FLAG
|
||||
|
||||
#echo "continue?"
|
||||
#read
|
||||
make -j$COMPILATION_PROC_COUNT
|
||||
make install
|
||||
|
||||
}
|
||||
|
||||
function setCurrentPlatform {
|
||||
|
||||
PLATFORM="$(uname -s)"
|
||||
case "${PLATFORM}" in
|
||||
Darwin*)
|
||||
BUILD_PLATFORM=darwin-x86_64
|
||||
COMPILATION_PROC_COUNT=`sysctl -n hw.physicalcpu`
|
||||
;;
|
||||
Linux*)
|
||||
BUILD_PLATFORM=linux-x86_64
|
||||
COMPILATION_PROC_COUNT=$(nproc)
|
||||
;;
|
||||
*)
|
||||
echo -e "\033[33mWarning! Unknown platform ${PLATFORM}! falling back to linux-x86_64\033[0m"
|
||||
BUILD_PLATFORM=linux-x86_64
|
||||
COMPILATION_PROC_COUNT=1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "build platform: ${BUILD_PLATFORM}"
|
||||
echo "parallel jobs: ${COMPILATION_PROC_COUNT}"
|
||||
|
||||
}
|
||||
|
||||
function checkPreRequisites {
|
||||
|
||||
if ! [ -d "ffmpeg" ] || ! [ "$(ls -A ffmpeg)" ]; then
|
||||
echo -e "\033[31mFailed! Submodule 'ffmpeg' not found!\033[0m"
|
||||
echo -e "\033[31mTry to run: 'git submodule init && git submodule update'\033[0m"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "$NDK" -a "$NDK" == "" ]; then
|
||||
echo -e "\033[31mFailed! NDK is empty. Run 'export NDK=[PATH_TO_NDK]'\033[0m"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
setCurrentPlatform
|
||||
checkPreRequisites
|
||||
|
||||
# TODO: fix env variable for NDK
|
||||
# NDK=/opt/android-sdk/ndk-bundle
|
||||
|
||||
cd ffmpeg
|
||||
|
||||
#x86_64
|
||||
PREBUILT=$NDK/toolchains/x86_64-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-21/arch-x86_64
|
||||
LD=$PREBUILT/bin/x86_64-linux-android-ld
|
||||
AR=$PREBUILT/bin/x86_64-linux-android-ar
|
||||
NM=$PREBUILT/bin/x86_64-linux-android-nm
|
||||
GCCLIB=$PREBUILT/lib/gcc/x86_64-linux-android/4.9.x/libgcc.a
|
||||
CC=$PREBUILT/bin/x86_64-linux-android-gcc
|
||||
CROSS_PREFIX=$PREBUILT/bin/x86_64-linux-android-
|
||||
ARCH=x86_64
|
||||
CPU=x86_64
|
||||
PREFIX=./build/$CPU
|
||||
ADDITIONAL_CONFIGURE_FLAG="--disable-mmx --disable-inline-asm"
|
||||
#build_one
|
||||
|
||||
#arm64-v8a
|
||||
PREBUILT=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-21/arch-arm64
|
||||
LD=$PREBUILT/bin/aarch64-linux-android-ld
|
||||
AR=$PREBUILT/bin/aarch64-linux-android-ar
|
||||
NM=$PREBUILT/bin/aarch64-linux-android-nm
|
||||
GCCLIB=$PREBUILT/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a
|
||||
CC=$PREBUILT/bin/aarch64-linux-android-gcc
|
||||
CROSS_PREFIX=$PREBUILT/bin/aarch64-linux-android-
|
||||
ARCH=arm64
|
||||
CPU=arm64-v8a
|
||||
OPTIMIZE_CFLAGS=
|
||||
PREFIX=./build/$CPU
|
||||
ADDITIONAL_CONFIGURE_FLAG="--enable-neon --enable-optimizations"
|
||||
build_one
|
||||
|
||||
|
||||
#arm v7n
|
||||
PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-16/arch-arm
|
||||
LD=$PREBUILT/bin/arm-linux-androideabi-ld
|
||||
AR=$PREBUILT/bin/arm-linux-androideabi-ar
|
||||
NM=$PREBUILT/bin/arm-linux-androideabi-nm
|
||||
GCCLIB=$PREBUILT/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a
|
||||
CC=$PREBUILT/bin/arm-linux-androideabi-gcc
|
||||
CROSS_PREFIX=$PREBUILT/bin/arm-linux-androideabi-
|
||||
ARCH=arm
|
||||
CPU=armv7-a
|
||||
OPTIMIZE_CFLAGS="-marm -march=$CPU"
|
||||
PREFIX=./build/$CPU
|
||||
ADDITIONAL_CONFIGURE_FLAG=--enable-neon
|
||||
build_one
|
||||
|
||||
#x86 platform
|
||||
PREBUILT=$NDK/toolchains/x86-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-16/arch-x86
|
||||
LD=$PREBUILT/bin/i686-linux-android-ld
|
||||
AR=$PREBUILT/bin/i686-linux-android-ar
|
||||
NM=$PREBUILT/bin/i686-linux-android-nm
|
||||
GCCLIB=$PREBUILT/lib/gcc/i686-linux-android/4.9.x/libgcc.a
|
||||
CC=$PREBUILT/bin/i686-linux-android-gcc
|
||||
CROSS_PREFIX=$PREBUILT/bin/i686-linux-android-
|
||||
ARCH=x86
|
||||
CPU=i686
|
||||
OPTIMIZE_CFLAGS="-march=$CPU"
|
||||
PREFIX=./build/$CPU
|
||||
ADDITIONAL_CONFIGURE_FLAG="--disable-mmx --disable-yasm"
|
||||
build_one
|
|
@ -1,28 +1,36 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
function build_one {
|
||||
CC="${CROSS_PREFIX}clang"
|
||||
CXX="${CROSS_PREFIX}clang++"
|
||||
AS="${CROSS_PREFIX}clang"
|
||||
AR="${CROSS_PREFIX}ar"
|
||||
LD="${CROSS_PREFIX}ld"
|
||||
NM="${CROSS_PREFIX}nm"
|
||||
STRIP="${CROSS_PREFIX}strip"
|
||||
echo "Building ${ARCH}..."
|
||||
|
||||
PREBUILT=${NDK}/toolchains/${PREBUILT_ARCH}${PREBUILT_MIDDLE}-${VERSION}/prebuilt/${BUILD_PLATFORM}
|
||||
PLATFORM=${NDK}/platforms/android-${ANDROID_API}/arch-${ARCH}
|
||||
|
||||
echo "Toolchain..."
|
||||
python $NDK/build/tools/make_standalone_toolchain.py \
|
||||
--arch ${ARCH} \
|
||||
--api ${ANDROID_API} \
|
||||
--stl libc++ \
|
||||
--install-dir=${TOOLCHAIN_PREFIX}
|
||||
TOOLS_PREFIX="${LLVM_BIN}/${ARCH_NAME}-linux-${BIN_MIDDLE}-"
|
||||
|
||||
LD=${TOOLS_PREFIX}ld
|
||||
AR=${TOOLS_PREFIX}ar
|
||||
STRIP=${TOOLS_PREFIX}strip
|
||||
NM=${TOOLS_PREFIX}nm
|
||||
|
||||
CC_PREFIX="${LLVM_BIN}/${CLANG_PREFIX}-linux-${BIN_MIDDLE}${ANDROID_API}-"
|
||||
|
||||
CC=${CC_PREFIX}clang
|
||||
CXX=${CC_PREFIX}clang++
|
||||
CROSS_PREFIX=${PREBUILT}/bin/${ARCH_NAME}-linux-${BIN_MIDDLE}-
|
||||
|
||||
INCLUDES=" -I./${LIBVPXPREFIX}/include"
|
||||
LIBS=" -L./${LIBVPXPREFIX}/lib"
|
||||
|
||||
echo "Cleaning..."
|
||||
rm -f config.h
|
||||
make clean || true
|
||||
|
||||
echo "Configuring..."
|
||||
|
||||
./configure \
|
||||
--nm=${NM} \
|
||||
--ar=${AR} \
|
||||
--as=${CROSS_PREFIX}gcc \
|
||||
--strip=${STRIP} \
|
||||
--cc=${CC} \
|
||||
--cxx=${CXX} \
|
||||
|
@ -30,7 +38,7 @@ function build_one {
|
|||
--arch=$ARCH \
|
||||
--target-os=linux \
|
||||
--enable-cross-compile \
|
||||
--x86asmexe=$NDK/prebuilt/$BUILD_PLATFORM/bin/yasm \
|
||||
--x86asmexe=$NDK/prebuilt/${BUILD_PLATFORM}/bin/yasm \
|
||||
--prefix=$PREFIX \
|
||||
--enable-pic \
|
||||
--disable-shared \
|
||||
|
@ -39,9 +47,10 @@ function build_one {
|
|||
--enable-inline-asm \
|
||||
--enable-x86asm \
|
||||
--cross-prefix=$CROSS_PREFIX \
|
||||
--sysroot=$SYSROOT \
|
||||
--extra-cflags="-Wl,-Bsymbolic -Os -DCONFIG_LINUX_PERF=0 -DANDROID $OPTIMIZE_CFLAGS -fPIE -pie --static -fPIC" \
|
||||
--extra-ldflags="-Wl,-Bsymbolic -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lc -lm -ldl -fPIC" \
|
||||
--sysroot="${LLVM_PREFIX}/sysroot" \
|
||||
--extra-cflags="${INCLUDES} -Wl,-Bsymbolic -Os -DCONFIG_LINUX_PERF=0 -DANDROID $OPTIMIZE_CFLAGS -fPIE -pie --static -fPIC" \
|
||||
--extra-cxxflags="${INCLUDES} -Wl,-Bsymbolic -Os -DCONFIG_LINUX_PERF=0 -DANDROID $OPTIMIZE_CFLAGS -fPIE -pie --static -fPIC" \
|
||||
--extra-ldflags="${LIBS} -Wl,-Bsymbolic -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -lc -lm -ldl -fPIC" \
|
||||
\
|
||||
--enable-version3 \
|
||||
--enable-gpl \
|
||||
|
@ -60,17 +69,18 @@ function build_one {
|
|||
--disable-postproc \
|
||||
--disable-debug \
|
||||
--disable-programs \
|
||||
--disable-network \
|
||||
--disable-ffplay \
|
||||
--disable-ffprobe \
|
||||
--disable-postproc \
|
||||
--disable-avdevice \
|
||||
\
|
||||
--enable-libvpx \
|
||||
--enable-decoder=libvpx_vp9 \
|
||||
--enable-runtime-cpudetect \
|
||||
--enable-pthreads \
|
||||
--enable-avresample \
|
||||
--enable-swscale \
|
||||
--enable-protocol=file \
|
||||
--enable-decoder=opus \
|
||||
--enable-decoder=h264 \
|
||||
--enable-decoder=mpeg4 \
|
||||
--enable-decoder=mjpeg \
|
||||
|
@ -78,6 +88,8 @@ function build_one {
|
|||
--enable-decoder=alac \
|
||||
--enable-demuxer=mov \
|
||||
--enable-demuxer=gif \
|
||||
--enable-demuxer=ogg \
|
||||
--enable-demuxer=matroska \
|
||||
--enable-hwaccels \
|
||||
$ADDITIONAL_CONFIGURE_FLAG
|
||||
|
||||
|
@ -85,18 +97,12 @@ function build_one {
|
|||
#read
|
||||
make -j$COMPILATION_PROC_COUNT
|
||||
make install
|
||||
|
||||
echo "Cleaning..."
|
||||
rm -f config.h
|
||||
make clean || true
|
||||
rm -rf ${TOOLCHAIN_PREFIX}
|
||||
|
||||
}
|
||||
|
||||
function setCurrentPlatform {
|
||||
|
||||
PLATFORM="$(uname -s)"
|
||||
case "${PLATFORM}" in
|
||||
CURRENT_PLATFORM="$(uname -s)"
|
||||
case "${CURRENT_PLATFORM}" in
|
||||
Darwin*)
|
||||
BUILD_PLATFORM=darwin-x86_64
|
||||
COMPILATION_PROC_COUNT=`sysctl -n hw.physicalcpu`
|
||||
|
@ -106,14 +112,14 @@ function setCurrentPlatform {
|
|||
COMPILATION_PROC_COUNT=$(nproc)
|
||||
;;
|
||||
*)
|
||||
echo -e "\033[33mWarning! Unknown platform ${PLATFORM}! falling back to linux-x86_64\033[0m"
|
||||
echo -e "\033[33mWarning! Unknown platform ${CURRENT_PLATFORM}! falling back to linux-x86_64\033[0m"
|
||||
BUILD_PLATFORM=linux-x86_64
|
||||
COMPILATION_PROC_COUNT=1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "build platform: ${BUILD_PLATFORM}"
|
||||
echo "parallel jobs: ${COMPILATION_PROC_COUNT}"
|
||||
echo "Build platform: ${BUILD_PLATFORM}"
|
||||
echo "Parallel jobs: ${COMPILATION_PROC_COUNT}"
|
||||
|
||||
}
|
||||
|
||||
|
@ -139,56 +145,85 @@ checkPreRequisites
|
|||
|
||||
cd ffmpeg
|
||||
|
||||
BASEDIR=`pwd`
|
||||
TOOLCHAIN_PREFIX=${BASEDIR}/toolchain-android
|
||||
|
||||
## common
|
||||
SYSROOT=${TOOLCHAIN_PREFIX}/sysroot
|
||||
LLVM_PREFIX="${NDK}/toolchains/llvm/prebuilt/linux-x86_64"
|
||||
LLVM_BIN="${LLVM_PREFIX}/bin"
|
||||
VERSION="4.9"
|
||||
|
||||
#x86_64
|
||||
function build {
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
x86_64)
|
||||
ANDROID_API=21
|
||||
PREBUILT=$NDK/toolchains/x86_64-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-21/arch-x86_64
|
||||
CROSS_PREFIX=${TOOLCHAIN_PREFIX}/bin/x86_64-linux-android-
|
||||
|
||||
ARCH=x86_64
|
||||
ARCH_NAME=x86_64
|
||||
PREBUILT_ARCH=x86_64
|
||||
PREBUILT_MIDDLE=
|
||||
CLANG_PREFIX=x86_64
|
||||
BIN_MIDDLE=android
|
||||
CPU=x86_64
|
||||
PREFIX=./build/$CPU
|
||||
LIBVPXPREFIX=../libvpx/build/x86_64
|
||||
ADDITIONAL_CONFIGURE_FLAG="--disable-asm"
|
||||
build_one
|
||||
|
||||
#arm64-v8a
|
||||
;;
|
||||
arm64)
|
||||
ANDROID_API=21
|
||||
PREBUILT=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-21/arch-arm64
|
||||
CROSS_PREFIX=${TOOLCHAIN_PREFIX}/bin/aarch64-linux-android-
|
||||
|
||||
ARCH=arm64
|
||||
ARCH_NAME=aarch64
|
||||
PREBUILT_ARCH=aarch64
|
||||
PREBUILT_MIDDLE="-linux-android"
|
||||
CLANG_PREFIX=aarch64
|
||||
BIN_MIDDLE=android
|
||||
CPU=arm64-v8a
|
||||
OPTIMIZE_CFLAGS=
|
||||
PREFIX=./build/$CPU
|
||||
LIBVPXPREFIX=../libvpx/build/arm64-v8a
|
||||
ADDITIONAL_CONFIGURE_FLAG="--enable-neon --enable-optimizations"
|
||||
build_one
|
||||
|
||||
|
||||
#arm v7n
|
||||
;;
|
||||
arm)
|
||||
ANDROID_API=16
|
||||
PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-16/arch-arm
|
||||
CROSS_PREFIX=${TOOLCHAIN_PREFIX}/bin/arm-linux-androideabi-
|
||||
|
||||
ARCH=arm
|
||||
ARCH_NAME=arm
|
||||
PREBUILT_ARCH=arm
|
||||
PREBUILT_MIDDLE="-linux-androideabi"
|
||||
CLANG_PREFIX=armv7a
|
||||
BIN_MIDDLE=androideabi
|
||||
CPU=armv7-a
|
||||
OPTIMIZE_CFLAGS="-marm -march=$CPU"
|
||||
PREFIX=./build/armeabi-v7a
|
||||
LIBVPXPREFIX=../libvpx/build/armeabi-v7a
|
||||
ADDITIONAL_CONFIGURE_FLAG="--enable-neon"
|
||||
build_one
|
||||
|
||||
#x86 platform
|
||||
;;
|
||||
x86)
|
||||
ANDROID_API=16
|
||||
PREBUILT=$NDK/toolchains/x86-4.9/prebuilt/$BUILD_PLATFORM
|
||||
PLATFORM=$NDK/platforms/android-16/arch-x86
|
||||
CROSS_PREFIX=${TOOLCHAIN_PREFIX}/bin/i686-linux-android-
|
||||
|
||||
ARCH=x86
|
||||
ARCH_NAME=i686
|
||||
PREBUILT_ARCH=x86
|
||||
PREBUILT_MIDDLE=
|
||||
CLANG_PREFIX=i686
|
||||
BIN_MIDDLE=android
|
||||
CPU=i686
|
||||
OPTIMIZE_CFLAGS="-march=$CPU"
|
||||
PREFIX=./build/x86
|
||||
LIBVPXPREFIX=../libvpx/build/x86
|
||||
ADDITIONAL_CONFIGURE_FLAG="--disable-x86asm --disable-inline-asm --disable-asm"
|
||||
build_one
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
if (( $# == 0 )); then
|
||||
build x86_64 arm64 arm x86
|
||||
else
|
||||
build $@
|
||||
fi
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
function build_one {
|
||||
echo "Building ${ARCH}..."
|
||||
|
||||
PREBUILT=${NDK}/toolchains/${PREBUILT_ARCH}-${VERSION}/prebuilt/${BUILD_PLATFORM}
|
||||
PLATFORM=${NDK}/platforms/android-${ANDROID_API}/arch-${ARCH}
|
||||
|
||||
TOOLS_PREFIX="${LLVM_BIN}/${ARCH_NAME}-linux-${BIN_MIDDLE}-"
|
||||
|
||||
export LD=${TOOLS_PREFIX}ld
|
||||
export AR=${TOOLS_PREFIX}ar
|
||||
export STRIP=${TOOLS_PREFIX}strip
|
||||
export RANLIB=${TOOLS_PREFIX}ranlib
|
||||
export NM=${TOOLS_PREFIX}nm
|
||||
|
||||
export CC_PREFIX="${LLVM_BIN}/${CLANG_PREFIX}-linux-${BIN_MIDDLE}${ANDROID_API}-"
|
||||
|
||||
export CC=${CC_PREFIX}clang
|
||||
export CXX=${CC_PREFIX}clang++
|
||||
export AS=${CC_PREFIX}clang++
|
||||
export CROSS_PREFIX=${PREBUILT}/bin/${ARCH_NAME}-linux-${BIN_MIDDLE}-
|
||||
|
||||
|
||||
export CFLAGS="-DANDROID -fpic -fpie ${OPTIMIZE_CFLAGS}"
|
||||
export CPPFLAGS="${CFLAGS}"
|
||||
export CXXFLAGS="${CFLAGS} -std=c++11"
|
||||
export ASFLAGS="-D__ANDROID__"
|
||||
export LDFLAGS="-L${PLATFORM}/usr/lib"
|
||||
|
||||
if [ "x86" = ${ARCH} ]; then
|
||||
patch -p1 < ../patches/libvpx_x86_fix.patch
|
||||
fi
|
||||
echo "Cleaning..."
|
||||
make clean || true
|
||||
|
||||
echo "Configuring..."
|
||||
|
||||
|
||||
|
||||
./configure \
|
||||
--extra-cflags="-isystem ${LLVM_PREFIX}/sysroot/usr/include/${ARCH_NAME}-linux-${BIN_MIDDLE} -isystem ${LLVM_PREFIX}/sysroot/usr/include ${OPTIMIZE_CFLAGS}" \
|
||||
--libc="${LLVM_PREFIX}/sysroot" \
|
||||
--prefix=${PREFIX} \
|
||||
--target=${TARGET} \
|
||||
${CPU_DETECT} \
|
||||
--as=yasm \
|
||||
--enable-static \
|
||||
--enable-pic \
|
||||
--disable-docs \
|
||||
--enable-libyuv \
|
||||
--enable-small \
|
||||
--enable-optimizations \
|
||||
--enable-better-hw-compatibility \
|
||||
--disable-examples \
|
||||
--disable-tools \
|
||||
--disable-debug \
|
||||
--disable-unit-tests \
|
||||
--disable-install-docs \
|
||||
--enable-realtime-only \
|
||||
--enable-vp8 \
|
||||
--enable-vp9 \
|
||||
--disable-webm-io
|
||||
|
||||
make -j$COMPILATION_PROC_COUNT install
|
||||
|
||||
if [ "x86" = ${ARCH} ]; then
|
||||
patch -p1 -R < ../patches/libvpx_x86_fix.patch
|
||||
fi
|
||||
}
|
||||
|
||||
function setCurrentPlatform {
|
||||
|
||||
CURRENT_PLATFORM="$(uname -s)"
|
||||
case "${CURRENT_PLATFORM}" in
|
||||
Darwin*)
|
||||
BUILD_PLATFORM=darwin-x86_64
|
||||
COMPILATION_PROC_COUNT=`sysctl -n hw.physicalcpu`
|
||||
;;
|
||||
Linux*)
|
||||
BUILD_PLATFORM=linux-x86_64
|
||||
COMPILATION_PROC_COUNT=$(nproc)
|
||||
;;
|
||||
*)
|
||||
echo -e "\033[33mWarning! Unknown platform ${CURRENT_PLATFORM}! falling back to linux-x86_64\033[0m"
|
||||
BUILD_PLATFORM=linux-x86_64
|
||||
COMPILATION_PROC_COUNT=1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Build platform: ${BUILD_PLATFORM}"
|
||||
echo "Parallel jobs: ${COMPILATION_PROC_COUNT}"
|
||||
|
||||
}
|
||||
|
||||
function checkPreRequisites {
|
||||
|
||||
if ! [ -d "libvpx" ] || ! [ "$(ls -A libvpx)" ]; then
|
||||
echo -e "\033[31mFailed! Submodule 'libvpx' not found!\033[0m"
|
||||
echo -e "\033[31mTry to run: 'git submodule init && git submodule update'\033[0m"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "$NDK" -a "$NDK" == "" ]; then
|
||||
echo -e "\033[31mFailed! NDK is empty. Run 'export NDK=[PATH_TO_NDK]'\033[0m"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
setCurrentPlatform
|
||||
checkPreRequisites
|
||||
|
||||
cd libvpx
|
||||
|
||||
## common
|
||||
LLVM_PREFIX="${NDK}/toolchains/llvm/prebuilt/linux-x86_64"
|
||||
LLVM_BIN="${LLVM_PREFIX}/bin"
|
||||
VERSION="4.9"
|
||||
ANDROID_API=21
|
||||
|
||||
function build {
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
x86_64)
|
||||
ANDROID_API=21
|
||||
ARCH=x86_64
|
||||
ARCH_NAME=x86_64
|
||||
PREBUILT_ARCH=x86_64
|
||||
CLANG_PREFIX=x86_64
|
||||
BIN_MIDDLE=android
|
||||
CPU=x86_64
|
||||
OPTIMIZE_CFLAGS="-O3 -march=x86-64 -mtune=intel -msse4.2 -mpopcnt -m64 -fPIC"
|
||||
TARGET="x86_64-android-gcc"
|
||||
PREFIX=./build/$CPU
|
||||
CPU_DETECT="--enable-runtime-cpu-detect"
|
||||
build_one
|
||||
;;
|
||||
x86)
|
||||
ANDROID_API=21
|
||||
ARCH=x86
|
||||
ARCH_NAME=i686
|
||||
PREBUILT_ARCH=x86
|
||||
CLANG_PREFIX=i686
|
||||
BIN_MIDDLE=android
|
||||
CPU=i686
|
||||
OPTIMIZE_CFLAGS="-O3 -march=i686 -mtune=intel -msse3 -mfpmath=sse -m32 -fPIC"
|
||||
TARGET="x86-android-gcc"
|
||||
PREFIX=./build/x86
|
||||
CPU_DETECT="--enable-runtime-cpu-detect"
|
||||
build_one
|
||||
;;
|
||||
arm64)
|
||||
ANDROID_API=21
|
||||
ARCH=arm64
|
||||
ARCH_NAME=aarch64
|
||||
PREBUILT_ARCH=aarch64
|
||||
CLANG_PREFIX=aarch64
|
||||
BIN_MIDDLE=android
|
||||
CPU=arm64-v8a
|
||||
OPTIMIZE_CFLAGS="-O3 -march=armv8-a"
|
||||
TARGET="arm64-android-gcc"
|
||||
PREFIX=./build/$CPU
|
||||
CPU_DETECT="--disable-runtime-cpu-detect"
|
||||
build_one
|
||||
;;
|
||||
arm)
|
||||
ANDROID_API=21
|
||||
ARCH=arm
|
||||
ARCH_NAME=arm
|
||||
PREBUILT_ARCH=arm
|
||||
CLANG_PREFIX=armv7a
|
||||
BIN_MIDDLE=androideabi
|
||||
CPU=armeabi-v7a
|
||||
OPTIMIZE_CFLAGS="-Os -D_LIBCPP_HAS_QUICK_EXIT -O3 -march=armv7-a -mfloat-abi=softfp -mfpu=neon -mtune=cortex-a8 -mthumb -D__thumb__"
|
||||
TARGET="armv7-android-gcc --enable-neon --disable-neon-asm"
|
||||
PREFIX=./build/$CPU
|
||||
CPU_DETECT="--disable-runtime-cpu-detect"
|
||||
build_one
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
if (( $# == 0 )); then
|
||||
build x86 x86_64 arm arm64
|
||||
else
|
||||
build $@
|
||||
fi
|
|
@ -1 +1 @@
|
|||
Subproject commit 89daac5fe2edb86f45fbd3281ac680bc598d6a97
|
||||
Subproject commit 4bc4cafaef8a55462138d7b6f7579c1522de26dc
|
|
@ -25,6 +25,8 @@ extern "C" {
|
|||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#define RGB8888_A(p) ((p & (0xff<<24)) >> 24 )
|
||||
|
||||
static const std::string av_make_error_str(int errnum) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
|
@ -119,6 +121,7 @@ typedef struct VideoInfo {
|
|||
} else {
|
||||
attached = false;
|
||||
}
|
||||
DEBUG_DELREF("gifvideocpp stream");
|
||||
jniEnv->DeleteGlobalRef(stream);
|
||||
if (attached) {
|
||||
javaVm->DetachCurrentThread();
|
||||
|
@ -194,6 +197,18 @@ void custom_log(void *ptr, int level, const char* fmt, va_list vl){
|
|||
LOGE(line);
|
||||
}
|
||||
|
||||
static enum AVPixelFormat get_format(AVCodecContext *ctx,
|
||||
const enum AVPixelFormat *pix_fmts)
|
||||
{
|
||||
const enum AVPixelFormat *p;
|
||||
|
||||
for (p = pix_fmts; *p != -1; p++) {
|
||||
LOGE("available format %d", p);
|
||||
}
|
||||
|
||||
return pix_fmts[0];
|
||||
}
|
||||
|
||||
int open_codec_context(int *stream_idx, AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type) {
|
||||
int ret, stream_index;
|
||||
AVStream *st;
|
||||
|
@ -776,8 +791,14 @@ int readCallback(void *opaque, uint8_t *buf, int buf_size) {
|
|||
if (attached) {
|
||||
javaVm->DetachCurrentThread();
|
||||
}
|
||||
if (buf_size == 0) {
|
||||
return AVERROR_EOF;
|
||||
}
|
||||
int ret = (int) read(info->fd, buf, (size_t) buf_size);
|
||||
return ret ? ret : AVERROR_EOF;
|
||||
if (ret <= 0) {
|
||||
return AVERROR_EOF;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -935,6 +956,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_AnimatedFileD
|
|||
int ret;
|
||||
if (streamFileSize != 0) {
|
||||
info->file_size = streamFileSize;
|
||||
DEBUG_REF("gifvideo.cpp new stream");
|
||||
info->stream = env->NewGlobalRef(stream);
|
||||
info->account = account;
|
||||
info->fd = open(info->src, O_RDONLY, S_IRUSR);
|
||||
|
@ -1137,6 +1159,11 @@ extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDr
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t premultiply_channel_value(const uint32_t pixel, const uint8_t offset, const float normalizedAlpha) {
|
||||
auto multipliedValue = ((pixel >> offset) & 0xFF) * normalizedAlpha;
|
||||
return ((uint32_t)std::min(multipliedValue, 255.0f)) << offset;
|
||||
}
|
||||
|
||||
static inline void writeFrameToBitmap(JNIEnv *env, VideoInfo *info, jintArray data, jobject bitmap, jint stride) {
|
||||
jint *dataArr = env->GetIntArrayElements(data, 0);
|
||||
int32_t wantedWidth;
|
||||
|
@ -1160,14 +1187,16 @@ static inline void writeFrameToBitmap(JNIEnv *env, VideoInfo *info, jintArray da
|
|||
void *pixels;
|
||||
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) {
|
||||
if (info->sws_ctx == nullptr) {
|
||||
if (info->frame->format > AV_PIX_FMT_NONE && info->frame->format < AV_PIX_FMT_NB) {
|
||||
if (info->frame->format > AV_PIX_FMT_NONE && info->frame->format < AV_PIX_FMT_NB && info->frame->format != AV_PIX_FMT_YUVA420P) {
|
||||
info->sws_ctx = sws_getContext(info->frame->width, info->frame->height, (AVPixelFormat) info->frame->format, bitmapWidth, bitmapHeight, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
|
||||
} else if (info->video_dec_ctx->pix_fmt > AV_PIX_FMT_NONE && info->video_dec_ctx->pix_fmt < AV_PIX_FMT_NB) {
|
||||
} else if (info->video_dec_ctx->pix_fmt > AV_PIX_FMT_NONE && info->video_dec_ctx->pix_fmt < AV_PIX_FMT_NB && info->frame->format != AV_PIX_FMT_YUVA420P) {
|
||||
info->sws_ctx = sws_getContext(info->video_dec_ctx->width, info->video_dec_ctx->height, info->video_dec_ctx->pix_fmt, bitmapWidth, bitmapHeight, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
if (info->sws_ctx == nullptr || ((intptr_t) pixels) % 16 != 0) {
|
||||
if (info->frame->format == AV_PIX_FMT_YUV444P) {
|
||||
if (info->frame->format == AV_PIX_FMT_YUVA420P) {
|
||||
libyuv::I420AlphaToARGBMatrix(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], info->frame->data[3], info->frame->linesize[3], (uint8_t *) pixels, bitmapWidth * 4, &libyuv::kYvuI601Constants, bitmapWidth, bitmapHeight, 1);
|
||||
} else if (info->frame->format == AV_PIX_FMT_YUV444P) {
|
||||
libyuv::H444ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, bitmapWidth * 4, bitmapWidth, bitmapHeight);
|
||||
} else if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_YUVJ420P) {
|
||||
if (info->frame->colorspace == AVColorSpace::AVCOL_SPC_BT709) {
|
||||
|
@ -1338,7 +1367,7 @@ extern "C" JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_AnimatedFileDr
|
|||
}
|
||||
if (got_frame) {
|
||||
//LOGD("decoded frame with w = %d, h = %d, format = %d", info->frame->width, info->frame->height, info->frame->format);
|
||||
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_BGRA || info->frame->format == AV_PIX_FMT_YUVJ420P || info->frame->format == AV_PIX_FMT_YUV444P) {
|
||||
if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_BGRA || info->frame->format == AV_PIX_FMT_YUVJ420P || info->frame->format == AV_PIX_FMT_YUV444P || info->frame->format == AV_PIX_FMT_YUVA420P) {
|
||||
writeFrameToBitmap(env, info, data, bitmap, stride);
|
||||
}
|
||||
info->has_decoded_frames = true;
|
||||
|
@ -1354,6 +1383,7 @@ extern "C" JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_AnimatedFileDr
|
|||
|
||||
extern "C" jint videoOnJNILoad(JavaVM *vm, JNIEnv *env) {
|
||||
//av_log_set_callback(custom_log);
|
||||
DEBUG_REF("gifvideo.cpp AnimatedFileDrawableStream ref");
|
||||
jclass_AnimatedFileDrawableStream = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/AnimatedFileDrawableStream"));
|
||||
if (jclass_AnimatedFileDrawableStream == 0) {
|
||||
return JNI_FALSE;
|
||||
|
|
|
@ -27,14 +27,17 @@ jfieldID jclass_Options_outHeight;
|
|||
jfieldID jclass_Options_outWidth;
|
||||
|
||||
jint imageOnJNILoad(JavaVM *vm, JNIEnv *env) {
|
||||
DEBUG_REF("image.cpp nullpointerexception class");
|
||||
jclass_NullPointerException = (jclass) env->NewGlobalRef(env->FindClass("java/lang/NullPointerException"));
|
||||
if (jclass_NullPointerException == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
DEBUG_REF("image.cpp runtimeexception class");
|
||||
jclass_RuntimeException = (jclass) env->NewGlobalRef(env->FindClass("java/lang/RuntimeException"));
|
||||
if (jclass_RuntimeException == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
DEBUG_REF("image.cpp bitmapfactoryoptions class");
|
||||
jclass_Options = (jclass) env->NewGlobalRef(env->FindClass("android/graphics/BitmapFactory$Options"));
|
||||
if (jclass_Options == 0) {
|
||||
return JNI_FALSE;
|
||||
|
@ -1081,6 +1084,9 @@ JNIEXPORT jint Java_org_telegram_messenger_Utilities_saveProgressiveJpeg(JNIEnv
|
|||
return 0;
|
||||
}
|
||||
const char *pathStr = env->GetStringUTFChars(path, 0);
|
||||
if (pathStr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
std::string filePath = std::string(pathStr);
|
||||
if (pathStr != 0) {
|
||||
env->ReleaseStringUTFChars(path, pathStr);
|
||||
|
@ -1224,7 +1230,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_generateGradient(JNIEnv *en
|
|||
float directPixelY;
|
||||
float centerDistanceY;
|
||||
float centerDistanceY2;
|
||||
int32_t colorsCount = colorsArray[12] == 0 ? 3 : 4;
|
||||
int32_t colorsCount = colorsArray[12] == 0 && colorsArray[13] == 0 && colorsArray[14] == 0 && colorsArray[15] == 0 ? 3 : 4;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (pixelCache == nullptr) {
|
||||
|
|
|
@ -36,6 +36,7 @@ static Shape cloud_extra_mask1;
|
|||
static Shape cloud_extra_mask2;
|
||||
static Shape cloud_extra_mask3;
|
||||
static Shape cloud_extra_mask4;
|
||||
static int surfaceCreated = 0;
|
||||
|
||||
static Shape cloud_cover;
|
||||
|
||||
|
@ -54,7 +55,7 @@ static TexturedShape powerful_mask, powerful_infinity, powerful_infinity_white;
|
|||
|
||||
static Shape private_bg;
|
||||
|
||||
static TexturedShape telegram_sphere, telegram_plane;
|
||||
static TexturedShape telegram_sphere, telegram_plane, telegram_mask;
|
||||
|
||||
static Shape cloud_bg;
|
||||
|
||||
|
@ -72,7 +73,7 @@ static mat4x4 ribbons_layer;
|
|||
|
||||
static TexturedShape ic_bubble_dot, ic_bubble, ic_cam_lens, ic_cam, ic_pencil, ic_pin, ic_smile_eye, ic_smile, ic_videocam;
|
||||
static GLuint ic_bubble_dot_texture, ic_bubble_texture, ic_cam_lens_texture, ic_cam_texture, ic_pencil_texture, ic_pin_texture, ic_smile_eye_texture, ic_smile_texture, ic_videocam_texture;
|
||||
static GLuint telegram_sphere_texture, telegram_plane_texture;
|
||||
static GLuint telegram_sphere_texture, telegram_plane_texture, telegram_mask_texture;
|
||||
static GLuint fast_spiral_texture, fast_body_texture, fast_arrow_texture, fast_arrow_shadow_texture;
|
||||
static GLuint free_knot_up_texture, free_knot_down_texture;
|
||||
static GLuint powerful_mask_texture, powerful_star_texture, powerful_infinity_texture, powerful_infinity_white_texture;
|
||||
|
@ -105,6 +106,7 @@ static float scroll_offset;
|
|||
static float calculated_speedometer_sin;
|
||||
float ms0_anim;
|
||||
int fps_anim;
|
||||
int last_stars_update_fps;
|
||||
int count_anim_fps;
|
||||
static float speedometer_scroll_offset = 0, free_scroll_offset = 0, private_scroll_offset = 0;
|
||||
float anim_pencil_start_time, anim_pencil_start_all_time, anim_pencil_start_all_end_time;
|
||||
|
@ -125,6 +127,8 @@ static int32_t anim_pencil_period;
|
|||
static mat4x4 private_matrix;
|
||||
float cloud_scroll_offset;
|
||||
|
||||
vec4 background_color = {1, 1, 1, 1};
|
||||
|
||||
static inline void vec2_add(vec2 r, vec2 a, vec2 b) {
|
||||
int32_t i;
|
||||
for (i = 0; i < 2; ++i) {
|
||||
|
@ -1122,6 +1126,8 @@ xyz star_initial_position(int32_t randZ, int32_t forward) {
|
|||
}
|
||||
|
||||
void draw_stars() {
|
||||
int update = last_stars_update_fps != fps_anim;
|
||||
last_stars_update_fps = fps_anim;
|
||||
float k = (float) width / (float) height;
|
||||
|
||||
set_y_offset_objects(-100 * k * 0);
|
||||
|
@ -1137,7 +1143,9 @@ void draw_stars() {
|
|||
}
|
||||
|
||||
float speed = stars_scroll_offset + transition_speed;
|
||||
if (update) {
|
||||
stars[i].position.z += speed;
|
||||
}
|
||||
|
||||
if (stars[i].position.z > 0 && speed > 0) {
|
||||
stars[i].position = star_initial_position(0, 1);
|
||||
|
@ -1682,8 +1690,29 @@ void draw_safe(int32_t type, float alpha, float screw_alpha) {
|
|||
draw_textured_shape(&private_screw, private_matrix, NORMAL_ONE);
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_messenger_Intro_onDrawFrame(JNIEnv *env, jclass class) {
|
||||
time_local += 0.016f;
|
||||
JNIEXPORT void Java_org_telegram_messenger_Intro_setBackgroundColor(JNIEnv *env, jclass class, jfloat r, jfloat g, jfloat b, jfloat a) {
|
||||
background_color[0] = r;
|
||||
background_color[1] = g;
|
||||
background_color[2] = b;
|
||||
background_color[3] = a;
|
||||
|
||||
cloud_cover = create_rectangle(CSizeMake(240, 100), background_color);
|
||||
cloud_cover.params.anchor.y = -50;
|
||||
|
||||
TexturedShape was_mask = powerful_mask;
|
||||
powerful_mask = create_textured_rectangle(CSizeMake(200, 200), powerful_mask_texture);
|
||||
powerful_mask.params = was_mask.params;
|
||||
|
||||
telegram_mask = create_textured_rectangle(CSizeMake(200, 150), telegram_mask_texture);
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_messenger_Intro_onDrawFrame(JNIEnv *env, jclass class, jint deltaMs) {
|
||||
if (surfaceCreated == 0) {
|
||||
glClearColor(background_color[0], background_color[1], background_color[2], background_color[3]);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
return;
|
||||
}
|
||||
time_local += (float) deltaMs / 1000;
|
||||
|
||||
if (current_page != prev_page) {
|
||||
reset_ic();
|
||||
|
@ -1722,7 +1751,8 @@ JNIEXPORT void Java_org_telegram_messenger_Intro_onDrawFrame(JNIEnv *env, jclass
|
|||
}
|
||||
}
|
||||
|
||||
fps_anim++;
|
||||
// Normalize if FPS is greater than 60
|
||||
fps_anim = (int)(time_local / 0.016f);
|
||||
if (count_anim_fps == 1 && date - ms0_anim >= duration_const) {
|
||||
count_anim_fps = 0;
|
||||
}
|
||||
|
@ -1735,7 +1765,7 @@ JNIEXPORT void Java_org_telegram_messenger_Intro_onDrawFrame(JNIEnv *env, jclass
|
|||
|
||||
float private_back_k = .8;
|
||||
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClearColor(background_color[0], background_color[1], background_color[2], background_color[3]);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
|
||||
|
@ -2140,15 +2170,16 @@ JNIEXPORT void Java_org_telegram_messenger_Intro_onDrawFrame(JNIEnv *env, jclass
|
|||
draw_textured_shape(&telegram_sphere, main_matrix, NORMAL);
|
||||
|
||||
float tt = MINf(0, (float) (-M_PI * 125.0f / 180.0f + time * M_PI * 2 * 1.5f));
|
||||
float dx = sinf(tt) * 75;
|
||||
float dy = -sinf(tt) * 60;
|
||||
float dx = t(-75, 0, 0, 0.15f, EaseIn);
|
||||
float dy = t(75, 0, 0, 0.15f, EaseIn);
|
||||
telegram_plane.params.position = xyzMake(dx, dy, 0);
|
||||
float scale = (cosf(tt) + 1) * 0.5f;
|
||||
telegram_plane.params.scale = xyzMake(cosf(tt) * scale, scale, 1);
|
||||
float scale = t(0.1f, 1, 0.03f, 0.15f, EaseOut);
|
||||
telegram_plane.params.scale = xyzMake(scale, scale, 1);
|
||||
|
||||
if (tt < D2R(125)) {
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
draw_textured_shape(&telegram_plane, main_matrix, NORMAL_ONE);
|
||||
draw_textured_shape(&telegram_mask, main_matrix, NORMAL);
|
||||
}
|
||||
}
|
||||
} else if (current_page == 1) {
|
||||
|
@ -2159,18 +2190,18 @@ JNIEXPORT void Java_org_telegram_messenger_Intro_onDrawFrame(JNIEnv *env, jclass
|
|||
|
||||
double tt = time * M_PI * 2 * 1.5f;
|
||||
|
||||
float dx = (float) sin(tt) * 75;
|
||||
float dy = (float) -sin(tt) * 60;
|
||||
float dx = t(0, 75, 0, 0.15f, EaseOut);
|
||||
float dy = t(0, -75, 0, 0.15f, EaseOut);
|
||||
|
||||
telegram_plane.params.position = xyzMake(dx, dy, 0);
|
||||
|
||||
float scale = (float) (cos(tt) + 1) * 0.5f;
|
||||
|
||||
telegram_plane.params.scale = xyzMake((float) cos(tt) * scale, scale, 1);
|
||||
float scale = t(1, 0.1f, 0.03f, 0.15f, EaseOut);
|
||||
telegram_plane.params.scale = xyzMake(scale, scale, 1);
|
||||
|
||||
if (tt < D2R(125)) {
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
draw_textured_shape(&telegram_plane, main_matrix, NORMAL_ONE);
|
||||
draw_textured_shape(&telegram_mask, main_matrix, NORMAL);
|
||||
}
|
||||
}
|
||||
} else if (current_page == 2) {
|
||||
|
@ -2601,9 +2632,10 @@ JNIEXPORT void Java_org_telegram_messenger_Intro_setIcTextures(JNIEnv *env, jcla
|
|||
ic_videocam_texture = a_ic_videocam;
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_messenger_Intro_setTelegramTextures(JNIEnv *env, jclass class, GLuint a_telegram_sphere, GLuint a_telegram_plane) {
|
||||
JNIEXPORT void Java_org_telegram_messenger_Intro_setTelegramTextures(JNIEnv *env, jclass class, GLuint a_telegram_sphere, GLuint a_telegram_plane, GLuint a_telegram_mask) {
|
||||
telegram_sphere_texture = a_telegram_sphere;
|
||||
telegram_plane_texture = a_telegram_plane;
|
||||
telegram_mask_texture = a_telegram_mask;
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_messenger_Intro_setFastTextures(JNIEnv *env, jclass class, GLuint a_fast_body, GLuint a_fast_spiral, GLuint a_fast_arrow, GLuint a_fast_arrow_shadow) {
|
||||
|
@ -2691,6 +2723,7 @@ JNIEXPORT void Java_org_telegram_messenger_Intro_onSurfaceCreated(JNIEnv *env, j
|
|||
mask1 = create_rounded_rectangle(CSizeMake(60, 60), 0, 16, black_color);
|
||||
|
||||
telegram_sphere = create_textured_rectangle(CSizeMake(150, 150), telegram_sphere_texture);
|
||||
telegram_mask = create_textured_rectangle(CSizeMake(200, 150), telegram_mask_texture);
|
||||
telegram_plane = create_textured_rectangle(CSizeMake(82, 74), telegram_plane_texture);
|
||||
telegram_plane.params.anchor = xyzMake(6, -5, 0);
|
||||
|
||||
|
@ -2794,11 +2827,12 @@ JNIEXPORT void Java_org_telegram_messenger_Intro_onSurfaceCreated(JNIEnv *env, j
|
|||
cloud_extra_mask3 = create_circle(1, cloud_polygons_count, black_color);
|
||||
cloud_extra_mask4 = create_circle(1, cloud_polygons_count, black_color);
|
||||
|
||||
cloud_cover = create_rectangle(CSizeMake(240, 100), white_color);
|
||||
cloud_cover = create_rectangle(CSizeMake(240, 100), background_color);
|
||||
cloud_cover.params.anchor.y = -50;
|
||||
|
||||
vec4 cloud_color = {42 / 255.0f, 180 / 255.0f, 247 / 255.0f, 1};
|
||||
cloud_bg = create_rectangle(CSizeMake(160 * 2, 160 * 2), cloud_color);
|
||||
surfaceCreated = 1;
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_messenger_Intro_onSurfaceChanged(JNIEnv *env, jclass class, int32_t a_width_px, int32_t a_height_px, float a_scale_factor, int32_t a1) {
|
||||
|
|
|
@ -111,7 +111,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCtrDecryption(JNIEnv *en
|
|||
(*env)->ReleaseByteArrayElements(env, iv, ivBuff, JNI_ABORT);
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCtrDecryptionByteArray(JNIEnv *env, jclass class, jbyteArray buffer, jbyteArray key, jbyteArray iv, jint offset, jint length, jint fileOffset) {
|
||||
JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCtrDecryptionByteArray(JNIEnv *env, jclass class, jbyteArray buffer, jbyteArray key, jbyteArray iv, jint offset, jlong length, jint fileOffset) {
|
||||
unsigned char *bufferBuff = (unsigned char *) (*env)->GetByteArrayElements(env, buffer, NULL);
|
||||
unsigned char *keyBuff = (unsigned char *) (*env)->GetByteArrayElements(env, key, NULL);
|
||||
unsigned char *ivBuff = (unsigned char *) (*env)->GetByteArrayElements(env, iv, NULL);
|
||||
|
@ -186,6 +186,14 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCbcEncryption(JNIEnv *en
|
|||
(*env)->ReleaseByteArrayElements(env, iv, ivBuff, JNI_ABORT);
|
||||
}
|
||||
|
||||
#define LISTDIR_DOCTYPE_ALL 0
|
||||
#define LISTDIR_DOCTYPE_OTHER_THAN_MUSIC 1
|
||||
#define LISTDIR_DOCTYPE_MUSIC 2
|
||||
|
||||
#define LISTDIR_DOCTYPE2_EMOJI 3
|
||||
#define LISTDIR_DOCTYPE2_TEMP 4
|
||||
#define LISTDIR_DOCTYPE2_OTHER 5
|
||||
|
||||
int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t time, uint8_t subdirs) {
|
||||
int64_t value = 0;
|
||||
DIR *dir;
|
||||
|
@ -199,15 +207,29 @@ int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t tim
|
|||
if (name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
if ((docType == 1 || docType == 2) && len > 4) {
|
||||
if (name[len - 4] == '.' && (
|
||||
((name[len - 3] == 'm' || name[len - 3] == 'M') && (name[len - 2] == 'p' || name[len - 2] == 'P') && (name[len - 1] == '3')) ||
|
||||
((name[len - 3] == 'm' || name[len - 3] == 'M') && (name[len - 2] == '4') && (name[len - 1] == 'a' || name[len - 1] == 'A'))
|
||||
)) {
|
||||
if (docType == 1) {
|
||||
continue;
|
||||
}
|
||||
} else if (docType == 2) {
|
||||
if (docType > 0 && len > 4) {
|
||||
int isMusic = (
|
||||
name[len - 4] == '.' && (
|
||||
((name[len - 3] == 'm' || name[len - 3] == 'M') && (name[len - 2] == 'p' || name[len - 2] == 'P') && (name[len - 1] == '3')) || // mp3
|
||||
((name[len - 3] == 'm' || name[len - 3] == 'M') && (name[len - 2] == '4') && (name[len - 1] == 'a' || name[len - 1] == 'A')) // m4a
|
||||
));
|
||||
int isEmoji = (
|
||||
name[len - 4] == '.' && (name[len - 3] == 't' || name[len - 3] == 'T') && (name[len - 2] == 'g' || name[len - 2] == 'G') && (name[len - 1] == 's' || name[len - 1] == 'S') || // tgs
|
||||
len > 5 && name[len - 5] == '.' && (name[len - 4] == 'w' || name[len - 4] == 'W') && (name[len - 3] == 'e' || name[len - 3] == 'E') && (name[len - 2] == 'b' || name[len - 2] == 'B') && (name[len - 1] == 'm' || name[len - 1] == 'M') // webm
|
||||
);
|
||||
int isTemp = (
|
||||
name[len - 4] == '.' && (name[len - 3] == 't' || name[len - 3] == 'T') && (name[len - 2] == 'm' || name[len - 2] == 'M') && (name[len - 1] == 'p' || name[len - 1] == 'P') || // tmp
|
||||
len > 5 && name[len - 5] == '.' && (name[len - 4] == 't' || name[len - 4] == 'T') && (name[len - 3] == 'e' || name[len - 3] == 'E') && (name[len - 2] == 'm' || name[len - 2] == 'M') && (name[len - 1] == 'p' || name[len - 1] == 'P') || // temp
|
||||
len > 8 && name[len - 8] == '.' && (name[len - 7] == 'p' || name[len - 7] == 'P') && (name[len - 6] == 'r' || name[len - 6] == 'R') && (name[len - 5] == 'e' || name[len - 5] == 'E') && (name[len - 4] == 'l' || name[len - 4] == 'L') && (name[len - 3] == 'o' || name[len - 3] == 'O') && (name[len - 2] == 'a' || name[len - 2] == 'A') && (name[len - 1] == 'd' || name[len - 1] == 'D') // preload
|
||||
);
|
||||
if (
|
||||
isMusic && docType == LISTDIR_DOCTYPE_OTHER_THAN_MUSIC ||
|
||||
!isMusic && docType == LISTDIR_DOCTYPE_MUSIC ||
|
||||
isEmoji && docType == LISTDIR_DOCTYPE2_OTHER ||
|
||||
!isEmoji && docType == LISTDIR_DOCTYPE2_EMOJI ||
|
||||
isTemp && docType == LISTDIR_DOCTYPE2_OTHER ||
|
||||
!isTemp && docType == LISTDIR_DOCTYPE2_TEMP
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -219,9 +241,9 @@ int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t tim
|
|||
value += listdir(buff, mode, docType, time, subdirs);
|
||||
}
|
||||
} else {
|
||||
stat(buff, &attrib);
|
||||
int rc = stat(buff, &attrib);
|
||||
if (mode == 0) {
|
||||
value += 512 * attrib.st_blocks;
|
||||
value += rc == 0 ? attrib.st_size : 0;
|
||||
} else if (mode == 1) {
|
||||
if (attrib.st_atim.tv_sec != 0) {
|
||||
if (attrib.st_atim.tv_sec < time) {
|
||||
|
@ -247,6 +269,20 @@ JNIEXPORT jlong Java_org_telegram_messenger_Utilities_getDirSize(JNIEnv *env, jc
|
|||
return value;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong Java_org_telegram_messenger_Utilities_getLastUsageFileTime(JNIEnv *env, jclass class, jstring path) {
|
||||
const char *fileName = (*env)->GetStringUTFChars(env, path, NULL);
|
||||
struct stat attrib;
|
||||
stat(fileName, &attrib);
|
||||
jlong value;
|
||||
if (attrib.st_atim.tv_sec > 316000000) {
|
||||
value = attrib.st_atim.tv_sec;
|
||||
} else {
|
||||
value = attrib.st_mtim.tv_sec;
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, path, fileName);
|
||||
return value;
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_messenger_Utilities_clearDir(JNIEnv *env, jclass class, jstring path, jint docType, jlong time, jboolean subdirs) {
|
||||
const char *fileName = (*env)->GetStringUTFChars(env, path, NULL);
|
||||
listdir(fileName, 1, docType, time, subdirs);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 626ff35955c2c35b806b3e0ecf551a1a8611cdbf
|
|
@ -1 +1 @@
|
|||
Subproject commit 47f64f6eddb9da0d1271c4af9a87ac78244f0b57
|
||||
Subproject commit 113968ca47b1d1affbfe88472364b15699e239d6
|
|
@ -39,9 +39,9 @@ typedef struct LottieInfo {
|
|||
volatile uint32_t maxFrameSize = 0;
|
||||
uint32_t imageSize = 0;
|
||||
uint32_t fileOffset = 0;
|
||||
uint32_t fileFrame = 0;
|
||||
bool nextFrameIsCacheFrame = false;
|
||||
|
||||
FILE *precacheFile = nullptr;
|
||||
char *compressBuffer = nullptr;
|
||||
const char *buffer = nullptr;
|
||||
bool firstFrame = false;
|
||||
|
@ -51,7 +51,7 @@ typedef struct LottieInfo {
|
|||
volatile uint32_t framesAvailableInCache = 0;
|
||||
};
|
||||
|
||||
JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *env, jclass clazz, jstring src, jstring json, jint w, jint h, jintArray data, jboolean precache, jintArray colorReplacement, jboolean limitFps) {
|
||||
JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *env, jclass clazz, jstring src, jstring json, jint w, jint h, jintArray data, jboolean precache, jintArray colorReplacement, jboolean limitFps, jint fitzModifier) {
|
||||
auto info = new LottieInfo();
|
||||
|
||||
std::map<int32_t, int32_t> *colors = nullptr;
|
||||
|
@ -71,16 +71,35 @@ JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *e
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
FitzModifier modifier = FitzModifier::None;
|
||||
switch (fitzModifier) {
|
||||
case 12:
|
||||
modifier = FitzModifier::Type12;
|
||||
break;
|
||||
case 3:
|
||||
modifier = FitzModifier::Type3;
|
||||
break;
|
||||
case 4:
|
||||
modifier = FitzModifier::Type4;
|
||||
break;
|
||||
case 5:
|
||||
modifier = FitzModifier::Type5;
|
||||
break;
|
||||
case 6:
|
||||
modifier = FitzModifier::Type6;
|
||||
break;
|
||||
}
|
||||
char const *srcString = env->GetStringUTFChars(src, nullptr);
|
||||
info->path = srcString;
|
||||
if (json != nullptr) {
|
||||
char const *jsonString = env->GetStringUTFChars(json, nullptr);
|
||||
if (jsonString) {
|
||||
info->animation = rlottie::Animation::loadFromData(jsonString, info->path, colors);
|
||||
info->animation = rlottie::Animation::loadFromData(jsonString, info->path, colors, modifier);
|
||||
env->ReleaseStringUTFChars(json, jsonString);
|
||||
}
|
||||
} else {
|
||||
info->animation = rlottie::Animation::loadFromFile(info->path, colors);
|
||||
info->animation = rlottie::Animation::loadFromFile(info->path, colors, modifier);
|
||||
}
|
||||
if (srcString) {
|
||||
env->ReleaseStringUTFChars(src, srcString);
|
||||
|
@ -127,6 +146,7 @@ JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *e
|
|||
info->maxFrameSize = maxFrameSize;
|
||||
fread(&(info->imageSize), sizeof(uint32_t), 1, precacheFile);
|
||||
info->fileOffset = 9;
|
||||
info->fileFrame = 0;
|
||||
utimensat(0, info->cacheFile.c_str(), nullptr, 0);
|
||||
}
|
||||
fclose(precacheFile);
|
||||
|
@ -222,124 +242,6 @@ JNIEXPORT void Java_org_telegram_ui_Components_RLottieDrawable_replaceColors(JNI
|
|||
}
|
||||
}
|
||||
|
||||
bool cacheWriteThreadCreated{false};
|
||||
LottieInfo *cacheWriteThreadTask{nullptr};
|
||||
bool cacheWriteThreadDone{false};
|
||||
std::thread worker;
|
||||
std::mutex cacheMutex;
|
||||
std::condition_variable cacheCv;
|
||||
|
||||
std::mutex cacheDoneMutex;
|
||||
std::condition_variable cacheDoneCv;
|
||||
std::atomic<bool> frameReady{false};
|
||||
|
||||
void CacheWriteThreadProc() {
|
||||
while (!cacheWriteThreadDone) {
|
||||
std::unique_lock<std::mutex> lk(cacheMutex);
|
||||
cacheCv.wait(lk, [] { return frameReady.load(); });
|
||||
std::lock_guard<std::mutex> lg(cacheDoneMutex);
|
||||
LottieInfo *task;
|
||||
if (cacheWriteThreadTask != nullptr) {
|
||||
task = cacheWriteThreadTask;
|
||||
cacheWriteThreadTask = nullptr;
|
||||
} else {
|
||||
task = nullptr;
|
||||
}
|
||||
lk.unlock();
|
||||
|
||||
if (task != nullptr) {
|
||||
auto size = (uint32_t) LZ4_compress_default(task->buffer, task->compressBuffer, task->bufferSize, task->compressBound);
|
||||
if (task->firstFrame) {
|
||||
task->firstFrameSize = size;
|
||||
task->fileOffset = 9 + sizeof(uint32_t) + task->firstFrameSize;
|
||||
}
|
||||
task->maxFrameSize = MAX(task->maxFrameSize, size);
|
||||
fwrite(&size, sizeof(uint32_t), 1, task->precacheFile);
|
||||
fwrite(task->compressBuffer, sizeof(uint8_t), size, task->precacheFile);
|
||||
|
||||
fflush(task->precacheFile);
|
||||
fsync(fileno(task->precacheFile));
|
||||
task->framesAvailableInCache++;
|
||||
}
|
||||
frameReady = false;
|
||||
cacheDoneCv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_org_telegram_ui_Components_RLottieDrawable_createCache(JNIEnv *env, jclass clazz, jlong ptr, jint w, jint h) {
|
||||
if (ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
auto info = (LottieInfo *) (intptr_t) ptr;
|
||||
|
||||
FILE *cacheFile = fopen(info->cacheFile.c_str(), "r+");
|
||||
if (cacheFile != nullptr) {
|
||||
uint8_t temp;
|
||||
size_t read = fread(&temp, sizeof(uint8_t), 1, cacheFile);
|
||||
fclose(cacheFile);
|
||||
if (read == 1 && temp != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cacheWriteThreadCreated) {
|
||||
cacheWriteThreadCreated = true;
|
||||
worker = std::thread(CacheWriteThreadProc);
|
||||
}
|
||||
|
||||
if (info->nextFrameIsCacheFrame && info->createCache && info->frameCount != 0) {
|
||||
info->precacheFile = fopen(info->cacheFile.c_str(), "w+");
|
||||
if (info->precacheFile != nullptr) {
|
||||
fseek(info->precacheFile, info->fileOffset = 9, SEEK_SET);
|
||||
info->maxFrameSize = 0;
|
||||
info->bufferSize = w * h * 4;
|
||||
info->imageSize = (uint32_t) w * h * 4;
|
||||
info->compressBound = LZ4_compressBound(info->bufferSize);
|
||||
info->compressBuffer = new char[info->compressBound];
|
||||
auto firstBuffer = new uint8_t[info->bufferSize];
|
||||
auto secondBuffer = new uint8_t[info->bufferSize];
|
||||
//long time = ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis();
|
||||
|
||||
Surface surface1((uint32_t *) firstBuffer, (size_t) w, (size_t) h, (size_t) w * 4);
|
||||
Surface surface2((uint32_t *) secondBuffer, (size_t) w, (size_t) h, (size_t) w * 4);
|
||||
int framesPerUpdate = !info->limitFps || info->fps < 60 ? 1 : 2;
|
||||
int num = 0;
|
||||
for (size_t a = 0; a < info->frameCount; a += framesPerUpdate) {
|
||||
Surface &surfaceToRender = num % 2 == 0 ? surface1 : surface2;
|
||||
num++;
|
||||
info->animation->renderSync(a, surfaceToRender, true);
|
||||
if (a != 0) {
|
||||
std::unique_lock<std::mutex> lk(cacheDoneMutex);
|
||||
cacheDoneCv.wait(lk, [] { return !frameReady.load(); });
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lg(cacheMutex);
|
||||
cacheWriteThreadTask = info;
|
||||
info->firstFrame = a == 0;
|
||||
info->buffer = (const char *) surfaceToRender.buffer();
|
||||
frameReady = true;
|
||||
cacheCv.notify_one();
|
||||
}
|
||||
std::unique_lock<std::mutex> lk(cacheDoneMutex);
|
||||
cacheDoneCv.wait(lk, [] { return !frameReady.load(); });
|
||||
|
||||
//DEBUG_D("sticker time = %d", (int) (ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis() - time));
|
||||
delete[] info->compressBuffer;
|
||||
delete[] firstBuffer;
|
||||
delete[] secondBuffer;
|
||||
fseek(info->precacheFile, 0, SEEK_SET);
|
||||
uint8_t byte = 1;
|
||||
fwrite(&byte, sizeof(uint8_t), 1, info->precacheFile);
|
||||
uint32_t maxFrameSize = info->maxFrameSize;
|
||||
fwrite(&maxFrameSize, sizeof(uint32_t), 1, info->precacheFile);
|
||||
fwrite(&info->imageSize, sizeof(uint32_t), 1, info->precacheFile);
|
||||
fflush(info->precacheFile);
|
||||
fsync(fileno(info->precacheFile));
|
||||
info->createCache = false;
|
||||
fclose(info->precacheFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint Java_org_telegram_ui_Components_RLottieDrawable_getFrame(JNIEnv *env, jclass clazz, jlong ptr, jint frame, jobject bitmap, jint w, jint h, jint stride, jboolean clear) {
|
||||
if (!ptr || bitmap == nullptr) {
|
||||
|
@ -347,59 +249,16 @@ JNIEXPORT jint Java_org_telegram_ui_Components_RLottieDrawable_getFrame(JNIEnv *
|
|||
}
|
||||
auto info = (LottieInfo *) (intptr_t) ptr;
|
||||
|
||||
int framesPerUpdate = !info->limitFps || info->fps < 60 ? 1 : 2;
|
||||
int framesAvailableInCache = info->framesAvailableInCache;
|
||||
|
||||
if (info->createCache && info->precache && frame > 0) {
|
||||
if (frame / framesPerUpdate >= framesAvailableInCache) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void *pixels;
|
||||
bool result = false;
|
||||
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) {
|
||||
bool loadedFromCache = false;
|
||||
uint32_t maxFrameSize = info->maxFrameSize;
|
||||
if (info->precache && (!info->createCache || frame > 0) && w * 4 == stride && maxFrameSize <= w * h * 4 && info->imageSize == w * h * 4) {
|
||||
FILE *precacheFile = fopen(info->cacheFile.c_str(), "r");
|
||||
if (precacheFile != nullptr) {
|
||||
if (info->decompressBuffer != nullptr && info->decompressBufferSize < maxFrameSize) {
|
||||
delete[] info->decompressBuffer;
|
||||
info->decompressBuffer = nullptr;
|
||||
}
|
||||
if (info->decompressBuffer == nullptr) {
|
||||
info->decompressBufferSize = maxFrameSize;
|
||||
if (info->createCache) {
|
||||
info->decompressBufferSize += 10000;
|
||||
}
|
||||
info->decompressBuffer = new uint8_t[info->decompressBufferSize];
|
||||
}
|
||||
fseek(precacheFile, info->fileOffset, SEEK_SET);
|
||||
uint32_t frameSize;
|
||||
fread(&frameSize, sizeof(uint32_t), 1, precacheFile);
|
||||
if (frameSize > 0 && frameSize <= info->decompressBufferSize) {
|
||||
fread(info->decompressBuffer, sizeof(uint8_t), frameSize, precacheFile);
|
||||
info->fileOffset += 4 + frameSize;
|
||||
LZ4_decompress_safe((const char *) info->decompressBuffer, (char *) pixels, frameSize, w * h * 4);
|
||||
loadedFromCache = true;
|
||||
}
|
||||
fclose(precacheFile);
|
||||
if (frame + framesPerUpdate >= info->frameCount) {
|
||||
info->fileOffset = 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadedFromCache) {
|
||||
if (!info->nextFrameIsCacheFrame || !info->precache) {
|
||||
Surface surface((uint32_t *) pixels, (size_t) w, (size_t) h, (size_t) stride);
|
||||
info->animation->renderSync((size_t) frame, surface, clear);
|
||||
info->nextFrameIsCacheFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
info->animation->renderSync((size_t) frame, surface, clear, &result);
|
||||
AndroidBitmap_unlockPixels(env, bitmap);
|
||||
}
|
||||
if (!result) {
|
||||
return -5;
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1083,6 +1083,9 @@ static opus_int64 op_predict_link_start(const OpusSeekRecord *_sr,int _nsr,
|
|||
offset2=_sr[srj].offset;
|
||||
/*For once, we can subtract with impunity.*/
|
||||
den=gp2-gp1;
|
||||
if (den == 0) {
|
||||
return -1;
|
||||
}
|
||||
ipart=gp2/den;
|
||||
num=offset2-offset1;
|
||||
OP_ASSERT(num>0);
|
||||
|
|
|
@ -3,27 +3,42 @@
|
|||
set -e
|
||||
|
||||
patch -d ffmpeg -p1 < patches/ffmpeg/0001-compilation-magic.patch
|
||||
patch -d ffmpeg -p1 < patches/ffmpeg/0002-compilation-magic-2.patch
|
||||
|
||||
libavformat=('dv.h' 'isom.h')
|
||||
libavcodec=('bytestream.h' 'get_bits.h' 'put_bits.h' 'golomb.h' 'vlc.h')
|
||||
libavutil=('intmath.h' 'reverse.h')
|
||||
function cp {
|
||||
install -D $@
|
||||
}
|
||||
|
||||
for arch in arm64-v8a armeabi-v7a x86 x86_64
|
||||
do
|
||||
for file in ${libavformat[*]}
|
||||
do
|
||||
cp ffmpeg/libavformat/"$file" ffmpeg/build/$arch/include/libavformat/"$file"
|
||||
done
|
||||
cp ffmpeg/libavformat/dv.h ffmpeg/build/arm64-v8a/include/libavformat/dv.h
|
||||
cp ffmpeg/libavformat/isom.h ffmpeg/build/arm64-v8a/include/libavformat/isom.h
|
||||
cp ffmpeg/libavformat/dv.h ffmpeg/build/armeabi-v7a/include/libavformat/dv.h
|
||||
cp ffmpeg/libavformat/isom.h ffmpeg/build/armeabi-v7a/include/libavformat/isom.h
|
||||
cp ffmpeg/libavformat/dv.h ffmpeg/build/x86/include/libavformat/dv.h
|
||||
cp ffmpeg/libavformat/isom.h ffmpeg/build/x86/include/libavformat/isom.h
|
||||
cp ffmpeg/libavformat/dv.h ffmpeg/build/x86_64/include/libavformat/dv.h
|
||||
cp ffmpeg/libavformat/isom.h ffmpeg/build/x86_64/include/libavformat/isom.h
|
||||
|
||||
# fix DrKLo's mystery include since 7.8.0
|
||||
for file in ${libavcodec[*]}
|
||||
do
|
||||
cp ffmpeg/libavcodec/"$file" ffmpeg/build/$arch/include/libavcodec/"$file"
|
||||
done
|
||||
for file in ${libavutil[*]}
|
||||
do
|
||||
cp ffmpeg/libavutil/"$file" ffmpeg/build/$arch/include/libavutil/"$file"
|
||||
done
|
||||
cp ffmpeg_mathops_fix.h ffmpeg/build/$arch/include/libavcodec/ffmpeg_mathops_fix.h
|
||||
sed -i 's/mathops/ffmpeg_mathops_fix/g' ffmpeg/build/$arch/include/libavcodec/get_bits.h
|
||||
done
|
||||
cp ffmpeg/libavcodec/bytestream.h ffmpeg/build/arm64-v8a/include/libavcodec/bytestream.h
|
||||
cp ffmpeg/libavcodec/bytestream.h ffmpeg/build/armeabi-v7a/include/libavcodec/bytestream.h
|
||||
cp ffmpeg/libavcodec/bytestream.h ffmpeg/build/x86/include/libavcodec/bytestream.h
|
||||
cp ffmpeg/libavcodec/bytestream.h ffmpeg/build/x86_64/include/libavcodec/bytestream.h
|
||||
|
||||
cp ffmpeg/libavcodec/get_bits.h ffmpeg/build/arm64-v8a/include/libavcodec/get_bits.h
|
||||
cp ffmpeg/libavcodec/get_bits.h ffmpeg/build/armeabi-v7a/include/libavcodec/get_bits.h
|
||||
cp ffmpeg/libavcodec/get_bits.h ffmpeg/build/x86/include/libavcodec/get_bits.h
|
||||
cp ffmpeg/libavcodec/get_bits.h ffmpeg/build/x86_64/include/libavcodec/get_bits.h
|
||||
|
||||
cp ffmpeg/libavcodec/golomb.h ffmpeg/build/arm64-v8a/include/libavcodec/golomb.h
|
||||
cp ffmpeg/libavcodec/golomb.h ffmpeg/build/armeabi-v7a/include/libavcodec/golomb.h
|
||||
cp ffmpeg/libavcodec/golomb.h ffmpeg/build/x86/include/libavcodec/golomb.h
|
||||
cp ffmpeg/libavcodec/golomb.h ffmpeg/build/x86_64/include/libavcodec/golomb.h
|
||||
|
||||
cp ffmpeg/libavcodec/vlc.h ffmpeg/build/arm64-v8a/include/libavcodec/vlc.h
|
||||
cp ffmpeg/libavcodec/vlc.h ffmpeg/build/armeabi-v7a/include/libavcodec/vlc.h
|
||||
cp ffmpeg/libavcodec/vlc.h ffmpeg/build/x86/include/libavcodec/vlc.h
|
||||
cp ffmpeg/libavcodec/vlc.h ffmpeg/build/x86_64/include/libavcodec/vlc.h
|
||||
|
||||
cp ffmpeg/libavutil/intmath.h ffmpeg/build/arm64-v8a/include/libavutil/intmath.h
|
||||
cp ffmpeg/libavutil/intmath.h ffmpeg/build/armeabi-v7a/include/libavutil/intmath.h
|
||||
cp ffmpeg/libavutil/intmath.h ffmpeg/build/x86/include/libavutil/intmath.h
|
||||
cp ffmpeg/libavutil/intmath.h ffmpeg/build/x86_64/include/libavutil/intmath.h
|
||||
|
|
|
@ -16,7 +16,7 @@ index 30823755..b349572c 100644
|
|||
+++ b/crypto/fipsmodule/CMakeLists.txt
|
||||
@@ -198,6 +198,8 @@ else()
|
||||
bcm.c
|
||||
is_fips.c
|
||||
fips_shared_support.c
|
||||
|
||||
+ aes/aes_ige.c
|
||||
+
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
From 02952ace408e331237a1ccd724f072b3e67ceb20 Mon Sep 17 00:00:00 2001
|
||||
From: thermatk <thermatk@thermatk.com>
|
||||
Date: Wed, 23 Jan 2019 22:16:34 +0100
|
||||
Subject: [PATCH] only build what we need
|
||||
|
||||
---
|
||||
CMakeLists.txt | 36 ---------------------
|
||||
crypto/CMakeLists.txt | 74 -------------------------------------------
|
||||
ssl/CMakeLists.txt | 18 ------------------
|
||||
3 files changed, 128 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index f58e853cd..a56305ce5 100644
|
||||
index fd3532664..7fcfb1627 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -564,44 +564,8 @@ if(USE_CUSTOM_LIBCXX)
|
||||
@@ -562,44 +562,8 @@ if(USE_CUSTOM_LIBCXX)
|
||||
target_link_libraries(libcxx libcxxabi)
|
||||
endif()
|
||||
|
||||
|
@ -47,11 +58,10 @@ index f58e853cd..a56305ce5 100644
|
|||
|
||||
if(FUZZ)
|
||||
if(LIBFUZZER_FROM_DEPS)
|
||||
@@ -617,39 +581,4 @@ endif()
|
||||
|
||||
@@ -589,38 +564,3 @@ endif()
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
set(HANDSHAKER_ARGS "-handshaker-path" $<TARGET_FILE:handshaker>)
|
||||
-endif()
|
||||
endif()
|
||||
-
|
||||
-if(FIPS)
|
||||
- add_custom_target(
|
||||
|
@ -87,13 +97,11 @@ index f58e853cd..a56305ce5 100644
|
|||
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
- DEPENDS all_tests bssl_shim handshaker fips_specific_tests_if_any
|
||||
- USES_TERMINAL)
|
||||
+endif()
|
||||
\ No newline at end of file
|
||||
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
|
||||
index cde92b591..1807d036a 100644
|
||||
index e940f7d5f..f5221d70d 100644
|
||||
--- a/crypto/CMakeLists.txt
|
||||
+++ b/crypto/CMakeLists.txt
|
||||
@@ -91,7 +91,6 @@ function(perlasm dest src)
|
||||
@@ -85,7 +85,6 @@ function(perlasm dest src)
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(fipsmodule)
|
||||
|
@ -101,10 +109,13 @@ index cde92b591..1807d036a 100644
|
|||
|
||||
if(FIPS_DELOCATE OR FIPS_SHARED)
|
||||
SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES EXTERNAL_OBJECT true)
|
||||
@@ -475,93 +474,4 @@ endif()
|
||||
|
||||
# urandom_test is a separate binary because it needs to be able to observe the
|
||||
# PRNG initialisation, which means that it can't have other tests running before
|
||||
@@ -466,96 +466,3 @@ endif()
|
||||
if(USE_CUSTOM_LIBCXX)
|
||||
target_link_libraries(crypto libcxx)
|
||||
endif()
|
||||
-
|
||||
-# urandom_test is a separate binary because it needs to be able to observe the
|
||||
-# PRNG initialisation, which means that it can't have other tests running before
|
||||
-# it does.
|
||||
-add_executable(
|
||||
- urandom_test
|
||||
|
@ -132,6 +143,7 @@ index cde92b591..1807d036a 100644
|
|||
- cipher_extra/cipher_test.cc
|
||||
- cmac/cmac_test.cc
|
||||
- compiler_test.cc
|
||||
- conf/conf_test.cc
|
||||
- constant_time_test.cc
|
||||
- cpu-arm-linux_test.cc
|
||||
- crypto_test.cc
|
||||
|
@ -182,7 +194,6 @@ index cde92b591..1807d036a 100644
|
|||
- x509/x509_test.cc
|
||||
- x509/x509_time_test.cc
|
||||
- x509v3/tab_test.cc
|
||||
- x509v3/v3name_test.cc
|
||||
-
|
||||
- $<TARGET_OBJECTS:crypto_test_data>
|
||||
- $<TARGET_OBJECTS:boringssl_gtest_main>
|
||||
|
@ -195,8 +206,6 @@ index cde92b591..1807d036a 100644
|
|||
- target_link_libraries(crypto_test ws2_32)
|
||||
-endif()
|
||||
-add_dependencies(all_tests crypto_test)
|
||||
+# it does.
|
||||
\ No newline at end of file
|
||||
diff --git a/ssl/CMakeLists.txt b/ssl/CMakeLists.txt
|
||||
index 0fb532eae..f5cab9807 100644
|
||||
--- a/ssl/CMakeLists.txt
|
||||
|
@ -223,3 +232,6 @@ index 0fb532eae..f5cab9807 100644
|
|||
- target_link_libraries(ssl_test ws2_32)
|
||||
-endif()
|
||||
-add_dependencies(all_tests ssl_test)
|
||||
--
|
||||
2.20.1
|
||||
|
||||
|
|
|
@ -0,0 +1,580 @@
|
|||
Index: libavcodec/golomb.h
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
diff --git a/libavcodec/golomb.h b/libavcodec/golomb.h
|
||||
--- a/libavcodec/golomb.h (revision a77521cd5d27e955b16e8097eecefc779ffdcb6d)
|
||||
+++ b/libavcodec/golomb.h (date 1647179346628)
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "get_bits.h"
|
||||
-#include "put_bits.h"
|
||||
|
||||
#define INVALID_VLC 0x80000000
|
||||
|
||||
@@ -422,144 +421,6 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
-/**
|
||||
- * read unsigned golomb rice code (jpegls).
|
||||
- */
|
||||
-static inline int get_ur_golomb_jpegls(GetBitContext *gb, int k, int limit,
|
||||
- int esc_len)
|
||||
-{
|
||||
- unsigned int buf;
|
||||
- int log;
|
||||
-
|
||||
-#if CACHED_BITSTREAM_READER
|
||||
- buf = show_bits_long(gb, 32);
|
||||
-
|
||||
- log = av_log2(buf);
|
||||
-
|
||||
- if (log - k >= 1 && 32 - log < limit) {
|
||||
- buf >>= log - k;
|
||||
- buf += (30 - log) << k;
|
||||
- skip_bits_long(gb, 32 + k - log);
|
||||
-
|
||||
- return buf;
|
||||
- } else {
|
||||
- int i;
|
||||
- for (i = 0;
|
||||
- i < limit && get_bits1(gb) == 0 && get_bits_left(gb) > 0;
|
||||
- i++);
|
||||
-
|
||||
- if (i < limit - 1) {
|
||||
- buf = get_bits_long(gb, k);
|
||||
-
|
||||
- return buf + (i << k);
|
||||
- } else if (i == limit - 1) {
|
||||
- buf = get_bits_long(gb, esc_len);
|
||||
-
|
||||
- return buf + 1;
|
||||
- } else
|
||||
- return -1;
|
||||
- }
|
||||
-#else
|
||||
- OPEN_READER(re, gb);
|
||||
- UPDATE_CACHE(re, gb);
|
||||
- buf = GET_CACHE(re, gb);
|
||||
-
|
||||
- log = av_log2(buf);
|
||||
-
|
||||
- av_assert2(k <= 31);
|
||||
-
|
||||
- if (log - k >= 32 - MIN_CACHE_BITS + (MIN_CACHE_BITS == 32) &&
|
||||
- 32 - log < limit) {
|
||||
- buf >>= log - k;
|
||||
- buf += (30U - log) << k;
|
||||
- LAST_SKIP_BITS(re, gb, 32 + k - log);
|
||||
- CLOSE_READER(re, gb);
|
||||
-
|
||||
- return buf;
|
||||
- } else {
|
||||
- int i;
|
||||
- for (i = 0; i + MIN_CACHE_BITS <= limit && SHOW_UBITS(re, gb, MIN_CACHE_BITS) == 0; i += MIN_CACHE_BITS) {
|
||||
- if (gb->size_in_bits <= re_index) {
|
||||
- CLOSE_READER(re, gb);
|
||||
- return -1;
|
||||
- }
|
||||
- LAST_SKIP_BITS(re, gb, MIN_CACHE_BITS);
|
||||
- UPDATE_CACHE(re, gb);
|
||||
- }
|
||||
- for (; i < limit && SHOW_UBITS(re, gb, 1) == 0; i++) {
|
||||
- SKIP_BITS(re, gb, 1);
|
||||
- }
|
||||
- LAST_SKIP_BITS(re, gb, 1);
|
||||
- UPDATE_CACHE(re, gb);
|
||||
-
|
||||
- if (i < limit - 1) {
|
||||
- if (k) {
|
||||
- if (k > MIN_CACHE_BITS - 1) {
|
||||
- buf = SHOW_UBITS(re, gb, 16) << (k-16);
|
||||
- LAST_SKIP_BITS(re, gb, 16);
|
||||
- UPDATE_CACHE(re, gb);
|
||||
- buf |= SHOW_UBITS(re, gb, k-16);
|
||||
- LAST_SKIP_BITS(re, gb, k-16);
|
||||
- } else {
|
||||
- buf = SHOW_UBITS(re, gb, k);
|
||||
- LAST_SKIP_BITS(re, gb, k);
|
||||
- }
|
||||
- } else {
|
||||
- buf = 0;
|
||||
- }
|
||||
-
|
||||
- buf += ((SUINT)i << k);
|
||||
- } else if (i == limit - 1) {
|
||||
- buf = SHOW_UBITS(re, gb, esc_len);
|
||||
- LAST_SKIP_BITS(re, gb, esc_len);
|
||||
-
|
||||
- buf ++;
|
||||
- } else {
|
||||
- buf = -1;
|
||||
- }
|
||||
- CLOSE_READER(re, gb);
|
||||
- return buf;
|
||||
- }
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * read signed golomb rice code (ffv1).
|
||||
- */
|
||||
-static inline int get_sr_golomb(GetBitContext *gb, int k, int limit,
|
||||
- int esc_len)
|
||||
-{
|
||||
- unsigned v = get_ur_golomb(gb, k, limit, esc_len);
|
||||
- return (v >> 1) ^ -(v & 1);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * read signed golomb rice code (flac).
|
||||
- */
|
||||
-static inline int get_sr_golomb_flac(GetBitContext *gb, int k, int limit,
|
||||
- int esc_len)
|
||||
-{
|
||||
- unsigned v = get_ur_golomb_jpegls(gb, k, limit, esc_len);
|
||||
- return (v >> 1) ^ -(v & 1);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * read unsigned golomb rice code (shorten).
|
||||
- */
|
||||
-static inline unsigned int get_ur_golomb_shorten(GetBitContext *gb, int k)
|
||||
-{
|
||||
- return get_ur_golomb_jpegls(gb, k, INT_MAX, 0);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * read signed golomb rice code (shorten).
|
||||
- */
|
||||
-static inline int get_sr_golomb_shorten(GetBitContext *gb, int k)
|
||||
-{
|
||||
- int uvar = get_ur_golomb_jpegls(gb, k + 1, INT_MAX, 0);
|
||||
- return (uvar >> 1) ^ -(uvar & 1);
|
||||
-}
|
||||
-
|
||||
#ifdef TRACE
|
||||
|
||||
static inline int get_ue(GetBitContext *s, const char *file, const char *func,
|
||||
@@ -614,134 +475,4 @@
|
||||
|
||||
#endif /* TRACE */
|
||||
|
||||
-/**
|
||||
- * write unsigned exp golomb code. 2^16 - 2 at most
|
||||
- */
|
||||
-static inline void set_ue_golomb(PutBitContext *pb, int i)
|
||||
-{
|
||||
- av_assert2(i >= 0);
|
||||
- av_assert2(i <= 0xFFFE);
|
||||
-
|
||||
- if (i < 256)
|
||||
- put_bits(pb, ff_ue_golomb_len[i], i + 1);
|
||||
- else {
|
||||
- int e = av_log2(i + 1);
|
||||
- put_bits(pb, 2 * e + 1, i + 1);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * write unsigned exp golomb code. 2^32-2 at most.
|
||||
- */
|
||||
-static inline void set_ue_golomb_long(PutBitContext *pb, uint32_t i)
|
||||
-{
|
||||
- av_assert2(i <= (UINT32_MAX - 1));
|
||||
-
|
||||
- if (i < 256)
|
||||
- put_bits(pb, ff_ue_golomb_len[i], i + 1);
|
||||
- else {
|
||||
- int e = av_log2(i + 1);
|
||||
- put_bits64(pb, 2 * e + 1, i + 1);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * write truncated unsigned exp golomb code.
|
||||
- */
|
||||
-static inline void set_te_golomb(PutBitContext *pb, int i, int range)
|
||||
-{
|
||||
- av_assert2(range >= 1);
|
||||
- av_assert2(i <= range);
|
||||
-
|
||||
- if (range == 2)
|
||||
- put_bits(pb, 1, i ^ 1);
|
||||
- else
|
||||
- set_ue_golomb(pb, i);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * write signed exp golomb code. 16 bits at most.
|
||||
- */
|
||||
-static inline void set_se_golomb(PutBitContext *pb, int i)
|
||||
-{
|
||||
- i = 2 * i - 1;
|
||||
- if (i < 0)
|
||||
- i ^= -1; //FIXME check if gcc does the right thing
|
||||
- set_ue_golomb(pb, i);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * write unsigned golomb rice code (ffv1).
|
||||
- */
|
||||
-static inline void set_ur_golomb(PutBitContext *pb, int i, int k, int limit,
|
||||
- int esc_len)
|
||||
-{
|
||||
- int e;
|
||||
-
|
||||
- av_assert2(i >= 0);
|
||||
-
|
||||
- e = i >> k;
|
||||
- if (e < limit)
|
||||
- put_bits(pb, e + k + 1, (1 << k) + av_mod_uintp2(i, k));
|
||||
- else
|
||||
- put_bits(pb, limit + esc_len, i - limit + 1);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * write unsigned golomb rice code (jpegls).
|
||||
- */
|
||||
-static inline void set_ur_golomb_jpegls(PutBitContext *pb, int i, int k,
|
||||
- int limit, int esc_len)
|
||||
-{
|
||||
- int e;
|
||||
-
|
||||
- av_assert2(i >= 0);
|
||||
-
|
||||
- e = (i >> k) + 1;
|
||||
- if (e < limit) {
|
||||
- while (e > 31) {
|
||||
- put_bits(pb, 31, 0);
|
||||
- e -= 31;
|
||||
- }
|
||||
- put_bits(pb, e, 1);
|
||||
- if (k)
|
||||
- put_sbits(pb, k, i);
|
||||
- } else {
|
||||
- while (limit > 31) {
|
||||
- put_bits(pb, 31, 0);
|
||||
- limit -= 31;
|
||||
- }
|
||||
- put_bits(pb, limit, 1);
|
||||
- put_bits(pb, esc_len, i - 1);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * write signed golomb rice code (ffv1).
|
||||
- */
|
||||
-static inline void set_sr_golomb(PutBitContext *pb, int i, int k, int limit,
|
||||
- int esc_len)
|
||||
-{
|
||||
- int v;
|
||||
-
|
||||
- v = -2 * i - 1;
|
||||
- v ^= (v >> 31);
|
||||
-
|
||||
- set_ur_golomb(pb, v, k, limit, esc_len);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * write signed golomb rice code (flac).
|
||||
- */
|
||||
-static inline void set_sr_golomb_flac(PutBitContext *pb, int i, int k,
|
||||
- int limit, int esc_len)
|
||||
-{
|
||||
- int v;
|
||||
-
|
||||
- v = -2 * i - 1;
|
||||
- v ^= (v >> 31);
|
||||
-
|
||||
- set_ur_golomb_jpegls(pb, v, k, limit, esc_len);
|
||||
-}
|
||||
-
|
||||
#endif /* AVCODEC_GOLOMB_H */
|
||||
Index: libavcodec/get_bits.h
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
diff --git a/libavcodec/get_bits.h b/libavcodec/get_bits.h
|
||||
--- a/libavcodec/get_bits.h (revision a77521cd5d27e955b16e8097eecefc779ffdcb6d)
|
||||
+++ b/libavcodec/get_bits.h (date 1647179363911)
|
||||
@@ -29,13 +29,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
-#include "libavutil/common.h"
|
||||
-#include "libavutil/intreadwrite.h"
|
||||
-#include "libavutil/log.h"
|
||||
-#include "libavutil/avassert.h"
|
||||
-#include "avcodec.h"
|
||||
-#include "mathops.h"
|
||||
-#include "vlc.h"
|
||||
+#ifndef NEG_USR32
|
||||
+# define NEG_USR32(a,s) (((uint32_t)(a))>>(32-(s)))
|
||||
+#endif
|
||||
|
||||
/*
|
||||
* Safe bitstream reading:
|
||||
@@ -312,73 +308,13 @@
|
||||
s->bits_left -= n;
|
||||
}
|
||||
#endif
|
||||
-
|
||||
-/**
|
||||
- * Read MPEG-1 dc-style VLC (sign bit + mantissa with no MSB).
|
||||
- * if MSB not set it is negative
|
||||
- * @param n length in bits
|
||||
- */
|
||||
-static inline int get_xbits(GetBitContext *s, int n)
|
||||
-{
|
||||
-#if CACHED_BITSTREAM_READER
|
||||
- int32_t cache = show_bits(s, 32);
|
||||
- int sign = ~cache >> 31;
|
||||
- skip_remaining(s, n);
|
||||
-
|
||||
- return ((((uint32_t)(sign ^ cache)) >> (32 - n)) ^ sign) - sign;
|
||||
-#else
|
||||
- register int sign;
|
||||
- register int32_t cache;
|
||||
- OPEN_READER(re, s);
|
||||
- av_assert2(n>0 && n<=25);
|
||||
- UPDATE_CACHE(re, s);
|
||||
- cache = GET_CACHE(re, s);
|
||||
- sign = ~cache >> 31;
|
||||
- LAST_SKIP_BITS(re, s, n);
|
||||
- CLOSE_READER(re, s);
|
||||
- return (NEG_USR32(sign ^ cache, n) ^ sign) - sign;
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
-#if !CACHED_BITSTREAM_READER
|
||||
-static inline int get_xbits_le(GetBitContext *s, int n)
|
||||
-{
|
||||
- register int sign;
|
||||
- register int32_t cache;
|
||||
- OPEN_READER(re, s);
|
||||
- av_assert2(n>0 && n<=25);
|
||||
- UPDATE_CACHE_LE(re, s);
|
||||
- cache = GET_CACHE(re, s);
|
||||
- sign = sign_extend(~cache, n) >> 31;
|
||||
- LAST_SKIP_BITS(re, s, n);
|
||||
- CLOSE_READER(re, s);
|
||||
- return (zero_extend(sign ^ cache, n) ^ sign) - sign;
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
-static inline int get_sbits(GetBitContext *s, int n)
|
||||
-{
|
||||
- register int tmp;
|
||||
-#if CACHED_BITSTREAM_READER
|
||||
- av_assert2(n>0 && n<=25);
|
||||
- tmp = sign_extend(get_bits(s, n), n);
|
||||
-#else
|
||||
- OPEN_READER(re, s);
|
||||
- av_assert2(n>0 && n<=25);
|
||||
- UPDATE_CACHE(re, s);
|
||||
- tmp = SHOW_SBITS(re, s, n);
|
||||
- LAST_SKIP_BITS(re, s, n);
|
||||
- CLOSE_READER(re, s);
|
||||
-#endif
|
||||
- return tmp;
|
||||
-}
|
||||
|
||||
/**
|
||||
* Read 1-25 bits.
|
||||
*/
|
||||
static inline unsigned int get_bits(GetBitContext *s, int n)
|
||||
{
|
||||
- register unsigned int tmp;
|
||||
+ unsigned int tmp;
|
||||
#if CACHED_BITSTREAM_READER
|
||||
|
||||
av_assert2(n>0 && n<=32);
|
||||
@@ -409,61 +345,6 @@
|
||||
return tmp;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * Read 0-25 bits.
|
||||
- */
|
||||
-static av_always_inline int get_bitsz(GetBitContext *s, int n)
|
||||
-{
|
||||
- return n ? get_bits(s, n) : 0;
|
||||
-}
|
||||
-
|
||||
-static inline unsigned int get_bits_le(GetBitContext *s, int n)
|
||||
-{
|
||||
-#if CACHED_BITSTREAM_READER
|
||||
- av_assert2(n>0 && n<=32);
|
||||
- if (n > s->bits_left) {
|
||||
- refill_32(s, 1);
|
||||
- if (s->bits_left < 32)
|
||||
- s->bits_left = n;
|
||||
- }
|
||||
-
|
||||
- return get_val(s, n, 1);
|
||||
-#else
|
||||
- register int tmp;
|
||||
- OPEN_READER(re, s);
|
||||
- av_assert2(n>0 && n<=25);
|
||||
- UPDATE_CACHE_LE(re, s);
|
||||
- tmp = SHOW_UBITS_LE(re, s, n);
|
||||
- LAST_SKIP_BITS(re, s, n);
|
||||
- CLOSE_READER(re, s);
|
||||
- return tmp;
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * Show 1-25 bits.
|
||||
- */
|
||||
-static inline unsigned int show_bits(GetBitContext *s, int n)
|
||||
-{
|
||||
- register unsigned int tmp;
|
||||
-#if CACHED_BITSTREAM_READER
|
||||
- if (n > s->bits_left)
|
||||
-#ifdef BITSTREAM_READER_LE
|
||||
- refill_32(s, 1);
|
||||
-#else
|
||||
- refill_32(s, 0);
|
||||
-#endif
|
||||
-
|
||||
- tmp = show_val(s, n);
|
||||
-#else
|
||||
- OPEN_READER_NOSIZE(re, s);
|
||||
- av_assert2(n>0 && n<=25);
|
||||
- UPDATE_CACHE(re, s);
|
||||
- tmp = SHOW_UBITS(re, s, n);
|
||||
-#endif
|
||||
- return tmp;
|
||||
-}
|
||||
-
|
||||
static inline void skip_bits(GetBitContext *s, int n)
|
||||
{
|
||||
#if CACHED_BITSTREAM_READER
|
||||
@@ -530,11 +411,6 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
-static inline unsigned int show_bits1(GetBitContext *s)
|
||||
-{
|
||||
- return show_bits(s, 1);
|
||||
-}
|
||||
-
|
||||
static inline void skip_bits1(GetBitContext *s)
|
||||
{
|
||||
skip_bits(s, 1);
|
||||
@@ -584,31 +460,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
-/**
|
||||
- * Read 0-32 bits as a signed integer.
|
||||
- */
|
||||
-static inline int get_sbits_long(GetBitContext *s, int n)
|
||||
-{
|
||||
- // sign_extend(x, 0) is undefined
|
||||
- if (!n)
|
||||
- return 0;
|
||||
-
|
||||
- return sign_extend(get_bits_long(s, n), n);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * Show 0-32 bits.
|
||||
- */
|
||||
-static inline unsigned int show_bits_long(GetBitContext *s, int n)
|
||||
-{
|
||||
- if (n <= MIN_CACHE_BITS) {
|
||||
- return show_bits(s, n);
|
||||
- } else {
|
||||
- GetBitContext gb = *s;
|
||||
- return get_bits_long(&gb, n);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
static inline int check_marker(void *logctx, GetBitContext *s, const char *msg)
|
||||
{
|
||||
int bit = get_bits1(s);
|
||||
@@ -772,62 +623,6 @@
|
||||
SKIP_BITS(name, gb, n); \
|
||||
} while (0)
|
||||
|
||||
-/* Return the LUT element for the given bitstream configuration. */
|
||||
-static inline int set_idx(GetBitContext *s, int code, int *n, int *nb_bits,
|
||||
- VLC_TYPE (*table)[2])
|
||||
-{
|
||||
- unsigned idx;
|
||||
-
|
||||
- *nb_bits = -*n;
|
||||
- idx = show_bits(s, *nb_bits) + code;
|
||||
- *n = table[idx][1];
|
||||
-
|
||||
- return table[idx][0];
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * Parse a vlc code.
|
||||
- * @param bits is the number of bits which will be read at once, must be
|
||||
- * identical to nb_bits in init_vlc()
|
||||
- * @param max_depth is the number of times bits bits must be read to completely
|
||||
- * read the longest vlc code
|
||||
- * = (max_vlc_length + bits - 1) / bits
|
||||
- * @returns the code parsed or -1 if no vlc matches
|
||||
- */
|
||||
-static av_always_inline int get_vlc2(GetBitContext *s, VLC_TYPE (*table)[2],
|
||||
- int bits, int max_depth)
|
||||
-{
|
||||
-#if CACHED_BITSTREAM_READER
|
||||
- int nb_bits;
|
||||
- unsigned idx = show_bits(s, bits);
|
||||
- int code = table[idx][0];
|
||||
- int n = table[idx][1];
|
||||
-
|
||||
- if (max_depth > 1 && n < 0) {
|
||||
- skip_remaining(s, bits);
|
||||
- code = set_idx(s, code, &n, &nb_bits, table);
|
||||
- if (max_depth > 2 && n < 0) {
|
||||
- skip_remaining(s, nb_bits);
|
||||
- code = set_idx(s, code, &n, &nb_bits, table);
|
||||
- }
|
||||
- }
|
||||
- skip_remaining(s, n);
|
||||
-
|
||||
- return code;
|
||||
-#else
|
||||
- int code;
|
||||
-
|
||||
- OPEN_READER(re, s);
|
||||
- UPDATE_CACHE(re, s);
|
||||
-
|
||||
- GET_VLC(code, re, s, table, bits, max_depth);
|
||||
-
|
||||
- CLOSE_READER(re, s);
|
||||
-
|
||||
- return code;
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
static inline int decode012(GetBitContext *gb)
|
||||
{
|
||||
int n;
|
||||
@@ -865,4 +660,14 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static inline unsigned int show_bits_long(GetBitContext *s, int n)
|
||||
+{
|
||||
+ if (n <= MIN_CACHE_BITS) {
|
||||
+ return show_bits(s, n);
|
||||
+ } else {
|
||||
+ GetBitContext gb = *s;
|
||||
+ return get_bits_long(&gb, n);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
#endif /* AVCODEC_GET_BITS_H */
|
|
@ -0,0 +1,17 @@
|
|||
Index: vpx_dsp/add_noise.c
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
diff --git a/vpx_dsp/add_noise.c b/vpx_dsp/add_noise.c
|
||||
--- a/vpx_dsp/add_noise.c (revision 626ff35955c2c35b806b3e0ecf551a1a8611cdbf)
|
||||
+++ b/vpx_dsp/add_noise.c (date 1643957537362)
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "vpx_dsp/postproc.h"
|
||||
#include "vpx_ports/mem.h"
|
||||
|
||||
+#define rand() ((int)lrand48())
|
||||
+
|
||||
void vpx_plane_add_noise_c(uint8_t *start, const int8_t *noise, int blackclamp,
|
||||
int whiteclamp, int width, int height, int pitch) {
|
||||
int i, j;
|
|
@ -52,6 +52,15 @@ struct LOTLayerNode;
|
|||
|
||||
namespace rlottie {
|
||||
|
||||
enum class FitzModifier {
|
||||
None,
|
||||
Type12,
|
||||
Type3,
|
||||
Type4,
|
||||
Type5,
|
||||
Type6
|
||||
};
|
||||
|
||||
struct Color {
|
||||
Color() = default;
|
||||
Color(float r, float g , float b):_r(r), _g(g), _b(b){}
|
||||
|
@ -258,7 +267,7 @@ public:
|
|||
* @internal
|
||||
*/
|
||||
static std::unique_ptr<Animation>
|
||||
loadFromFile(const std::string &path, std::map<int32_t, int32_t> *colorReplacement);
|
||||
loadFromFile(const std::string &path, std::map<int32_t, int32_t> *colorReplacement, FitzModifier fitzModifier);
|
||||
|
||||
/**
|
||||
* @brief Constructs an animation object from JSON string data.
|
||||
|
@ -273,7 +282,7 @@ public:
|
|||
* @internal
|
||||
*/
|
||||
static std::unique_ptr<Animation>
|
||||
loadFromData(std::string jsonData, const std::string &key, std::map<int32_t, int32_t> *colorReplacement, const std::string &resourcePath="");
|
||||
loadFromData(std::string jsonData, const std::string &key, std::map<int32_t, int32_t> *colorReplacement, FitzModifier fitzModifier = FitzModifier::None, const std::string &resourcePath="");
|
||||
|
||||
/**
|
||||
* @brief Returns default framerate of the Lottie resource.
|
||||
|
@ -345,7 +354,7 @@ public:
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
void renderSync(size_t frameNo, Surface &surface, bool clear);
|
||||
void renderSync(size_t frameNo, Surface &surface, bool clear, bool* result);
|
||||
|
||||
/**
|
||||
* @brief Returns root layer of the composition updated with
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
double frameRate() const { return mModel->frameRate(); }
|
||||
size_t totalFrame() const { return mModel->totalFrame(); }
|
||||
size_t frameAtPos(double pos) const { return mModel->frameAtPos(pos); }
|
||||
Surface render(size_t frameNo, const Surface &surface, bool clear);
|
||||
Surface render(size_t frameNo, const Surface &surface, bool clear, bool* result);
|
||||
const LOTLayerNode * renderTree(size_t frameNo, const VSize &size);
|
||||
|
||||
const LayerInfoList &layerInfoList() const
|
||||
|
@ -93,11 +93,12 @@ bool AnimationImpl::update(size_t frameNo, const VSize &size)
|
|||
return mCompItem->update(frameNo);
|
||||
}
|
||||
|
||||
Surface AnimationImpl::render(size_t frameNo, const Surface &surface, bool clear)
|
||||
Surface AnimationImpl::render(size_t frameNo, const Surface &surface, bool clear, bool* result)
|
||||
{
|
||||
bool renderInProgress = mRenderInProgress.load();
|
||||
if (renderInProgress) {
|
||||
vCritical << "Already Rendering Scheduled for this Animation";
|
||||
*result = false;
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
@ -106,6 +107,7 @@ Surface AnimationImpl::render(size_t frameNo, const Surface &surface, bool clear
|
|||
VSize(surface.drawRegionWidth(), surface.drawRegionHeight()));
|
||||
mCompItem->render(surface, clear);
|
||||
mRenderInProgress.store(false);
|
||||
*result = true;
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
@ -125,6 +127,7 @@ void AnimationImpl::init(const std::shared_ptr<LOTModel> &model)
|
|||
std::unique_ptr<Animation> Animation::loadFromData(
|
||||
std::string jsonData, const std::string &key,
|
||||
std::map<int32_t, int32_t> *colorReplacement,
|
||||
FitzModifier fitzModifier,
|
||||
const std::string &resourcePath)
|
||||
{
|
||||
if (jsonData.empty()) {
|
||||
|
@ -135,7 +138,7 @@ std::unique_ptr<Animation> Animation::loadFromData(
|
|||
LottieLoader loader;
|
||||
if (loader.loadFromData(std::move(jsonData), key,
|
||||
colorReplacement,
|
||||
(resourcePath.empty() ? " " : resourcePath))) {
|
||||
(resourcePath.empty() ? " " : resourcePath), fitzModifier)) {
|
||||
auto animation = std::unique_ptr<Animation>(new Animation);
|
||||
animation->colorMap = colorReplacement;
|
||||
animation->d->init(loader.model());
|
||||
|
@ -145,7 +148,7 @@ std::unique_ptr<Animation> Animation::loadFromData(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Animation> Animation::loadFromFile(const std::string &path, std::map<int32_t, int32_t> *colorReplacement)
|
||||
std::unique_ptr<Animation> Animation::loadFromFile(const std::string &path, std::map<int32_t, int32_t> *colorReplacement, FitzModifier fitzModifier)
|
||||
{
|
||||
if (path.empty()) {
|
||||
vWarning << "File path is empty";
|
||||
|
@ -153,7 +156,7 @@ std::unique_ptr<Animation> Animation::loadFromFile(const std::string &path, std:
|
|||
}
|
||||
|
||||
LottieLoader loader;
|
||||
if (loader.load(path, colorReplacement)) {
|
||||
if (loader.load(path, colorReplacement, fitzModifier)) {
|
||||
auto animation = std::unique_ptr<Animation>(new Animation);
|
||||
animation->colorMap = colorReplacement;
|
||||
animation->d->init(loader.model());
|
||||
|
@ -197,9 +200,9 @@ const LOTLayerNode *Animation::renderTree(size_t frameNo, size_t width,
|
|||
return d->renderTree(frameNo, VSize(width, height));
|
||||
}
|
||||
|
||||
void Animation::renderSync(size_t frameNo, Surface &surface, bool clear)
|
||||
void Animation::renderSync(size_t frameNo, Surface &surface, bool clear, bool* res)
|
||||
{
|
||||
d->render(frameNo, surface, clear);
|
||||
d->render(frameNo, surface, clear, res);
|
||||
}
|
||||
|
||||
const LayerInfoList &Animation::layers() const
|
||||
|
|
|
@ -75,7 +75,7 @@ static std::string dirname(const std::string &path)
|
|||
return std::string(path, 0, len);
|
||||
}
|
||||
|
||||
bool LottieLoader::load(const std::string &path, std::map<int32_t, int32_t> *colorReplacement)
|
||||
bool LottieLoader::load(const std::string &path, std::map<int32_t, int32_t> *colorReplacement, rlottie::FitzModifier fitzModifier)
|
||||
{
|
||||
mModel = LottieFileCache::instance().find(path);
|
||||
if (mModel) return true;
|
||||
|
@ -95,7 +95,7 @@ bool LottieLoader::load(const std::string &path, std::map<int32_t, int32_t> *col
|
|||
if (content.empty()) return false;
|
||||
|
||||
const char *str = content.c_str();
|
||||
LottieParser parser(const_cast<char *>(str), dirname(path).c_str(), colorReplacement);
|
||||
LottieParser parser(const_cast<char *>(str), dirname(path).c_str(), colorReplacement, fitzModifier);
|
||||
if (parser.hasParsingError()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -111,12 +111,12 @@ bool LottieLoader::load(const std::string &path, std::map<int32_t, int32_t> *col
|
|||
|
||||
bool LottieLoader::loadFromData(std::string &&jsonData, const std::string &key,
|
||||
std::map<int32_t, int32_t> *colorReplacement,
|
||||
const std::string &resourcePath)
|
||||
const std::string &resourcePath, rlottie::FitzModifier fitzModifier)
|
||||
{
|
||||
mModel = LottieFileCache::instance().find(key);
|
||||
if (mModel) return true;
|
||||
|
||||
LottieParser parser(const_cast<char *>(jsonData.c_str()), resourcePath.c_str(), colorReplacement);
|
||||
LottieParser parser(const_cast<char *>(jsonData.c_str()), resourcePath.c_str(), colorReplacement, fitzModifier);
|
||||
mModel = parser.model();
|
||||
|
||||
if (!mModel) return false;
|
||||
|
|
|
@ -22,13 +22,14 @@
|
|||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <rlottie.h>
|
||||
|
||||
class LOTModel;
|
||||
class LottieLoader
|
||||
{
|
||||
public:
|
||||
bool load(const std::string &filePath, std::map<int32_t, int32_t> *colorReplacement);
|
||||
bool loadFromData(std::string &&jsonData, const std::string &key, std::map<int32_t, int32_t> *colorReplacement, const std::string &resourcePath);
|
||||
bool load(const std::string &filePath, std::map<int32_t, int32_t> *colorReplacement, rlottie::FitzModifier fitzModifier);
|
||||
bool loadFromData(std::string &&jsonData, const std::string &key, std::map<int32_t, int32_t> *colorReplacement, const std::string &resourcePath, rlottie::FitzModifier fitzModifier);
|
||||
std::shared_ptr<LOTModel> model();
|
||||
private:
|
||||
std::shared_ptr<LOTModel> mModel;
|
||||
|
|
|
@ -182,8 +182,8 @@ protected:
|
|||
|
||||
class LottieParserImpl : protected LookaheadParserHandler {
|
||||
public:
|
||||
LottieParserImpl(char *str, const char *dir_path, std::map<int32_t, int32_t> *colorReplacement)
|
||||
: LookaheadParserHandler(str), mDirPath(dir_path), colorMap(colorReplacement)
|
||||
LottieParserImpl(char *str, const char *dir_path, std::map<int32_t, int32_t> *colorReplacement, rlottie::FitzModifier fitzModifier)
|
||||
: LookaheadParserHandler(str), mDirPath(dir_path), colorMap(colorReplacement), mFitzModifier(fitzModifier)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -270,6 +270,9 @@ public:
|
|||
void parseShapeProperty(LOTAnimatable<LottieShapeData> &obj);
|
||||
void parseDashProperty(LOTDashProperty &dash);
|
||||
|
||||
void parseFitzColorReplacements();
|
||||
void parseFitzColorReplacement();
|
||||
|
||||
std::shared_ptr<VInterpolator> interpolator(VPointF, VPointF, std::string);
|
||||
|
||||
LottieColor toColor(const char *str);
|
||||
|
@ -279,6 +282,7 @@ public:
|
|||
bool hasParsingError();
|
||||
|
||||
protected:
|
||||
const rlottie::FitzModifier mFitzModifier;
|
||||
std::unordered_map<std::string, std::shared_ptr<VInterpolator>>
|
||||
mInterpolatorCache;
|
||||
std::shared_ptr<LOTCompositionData> mComposition;
|
||||
|
@ -611,6 +615,8 @@ void LottieParserImpl::parseComposition() {
|
|||
parseAssets(comp);
|
||||
} else if (0 == strcmp(key, "layers")) {
|
||||
parseLayers(comp);
|
||||
} else if (0 == strcmp(key, "fitz")) {
|
||||
parseFitzColorReplacements();
|
||||
} else {
|
||||
#ifdef DEBUG_PARSER
|
||||
vWarning << "Composition Attribute Skipped : " << key;
|
||||
|
@ -659,6 +665,79 @@ void LottieParserImpl::parseAssets(LOTCompositionData *composition) {
|
|||
// update the precomp layers with the actual layer object
|
||||
}
|
||||
|
||||
void LottieParserImpl::parseFitzColorReplacements()
|
||||
{
|
||||
RAPIDJSON_ASSERT(PeekType() == kArrayType);
|
||||
EnterArray();
|
||||
while (NextArrayValue()) {
|
||||
parseFitzColorReplacement();
|
||||
}
|
||||
}
|
||||
|
||||
void LottieParserImpl::parseFitzColorReplacement()
|
||||
{
|
||||
uint32_t original = 0;
|
||||
uint32_t type12 = 0;
|
||||
uint32_t type3 = 0;
|
||||
uint32_t type4 = 0;
|
||||
uint32_t type5 = 0;
|
||||
uint32_t type6 = 0;
|
||||
|
||||
EnterObject();
|
||||
while (const char *key = NextObjectKey()) {
|
||||
if (0 == strcmp(key, "o")) {
|
||||
RAPIDJSON_ASSERT(PeekType() == kNumberType);
|
||||
original = GetInt();
|
||||
} else if (0 == strcmp(key, "f12")) {
|
||||
RAPIDJSON_ASSERT(PeekType() == kNumberType);
|
||||
type12 = GetInt();
|
||||
} else if (0 == strcmp(key, "f3")) {
|
||||
RAPIDJSON_ASSERT(PeekType() == kNumberType);
|
||||
type3 = GetInt();
|
||||
} else if (0 == strcmp(key, "f4")) {
|
||||
RAPIDJSON_ASSERT(PeekType() == kNumberType);
|
||||
type4 = GetInt();
|
||||
} else if (0 == strcmp(key, "f5")) {
|
||||
RAPIDJSON_ASSERT(PeekType() == kNumberType);
|
||||
type5 = GetInt();
|
||||
} else if (0 == strcmp(key, "f6")) {
|
||||
RAPIDJSON_ASSERT(PeekType() == kNumberType);
|
||||
type6 = GetInt();
|
||||
} else {
|
||||
Skip(key);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t replacedType = 0;
|
||||
|
||||
switch (mFitzModifier) {
|
||||
case rlottie::FitzModifier::None:
|
||||
break;
|
||||
case rlottie::FitzModifier::Type12:
|
||||
replacedType = type12;
|
||||
break;
|
||||
case rlottie::FitzModifier::Type3:
|
||||
replacedType = type3;
|
||||
break;
|
||||
case rlottie::FitzModifier::Type4:
|
||||
replacedType = type4;
|
||||
break;
|
||||
case rlottie::FitzModifier::Type5:
|
||||
replacedType = type5;
|
||||
break;
|
||||
case rlottie::FitzModifier::Type6:
|
||||
replacedType = type6;
|
||||
break;
|
||||
}
|
||||
|
||||
if (replacedType != 0) {
|
||||
if (colorMap == NULL) {
|
||||
colorMap = new std::map<int32_t, int32_t>();
|
||||
}
|
||||
colorMap->insert(std::pair<int32_t, int32_t>(original, replacedType));
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr const unsigned char B64index[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
@ -2654,8 +2733,8 @@ LottieParser::~LottieParser()
|
|||
delete d;
|
||||
}
|
||||
|
||||
LottieParser::LottieParser(char *str, const char *dir_path, std::map<int32_t, int32_t> *colorReplacement)
|
||||
: d(new LottieParserImpl(str, dir_path, colorReplacement))
|
||||
LottieParser::LottieParser(char *str, const char *dir_path, std::map<int32_t, int32_t> *colorReplacement, rlottie::FitzModifier fitzModifier)
|
||||
: d(new LottieParserImpl(str, dir_path, colorReplacement, fitzModifier))
|
||||
{
|
||||
d->parseComposition();
|
||||
if (d->hasParsingError()) {
|
||||
|
|
|
@ -21,12 +21,13 @@
|
|||
|
||||
#include "lottiemodel.h"
|
||||
#include <map>
|
||||
#include <rlottie.h>
|
||||
|
||||
class LottieParserImpl;
|
||||
class LottieParser {
|
||||
public:
|
||||
~LottieParser();
|
||||
LottieParser(char* str, const char *dir_path, std::map<int32_t, int32_t> *colorReplacement);
|
||||
LottieParser(char* str, const char *dir_path, std::map<int32_t, int32_t> *colorReplacement, rlottie::FitzModifier fitzModifier = rlottie::FitzModifier::None);
|
||||
std::shared_ptr<LOTModel> model();
|
||||
bool hasParsingError();
|
||||
private:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -43,7 +43,30 @@ extern "C" {
|
|||
|
||||
|
||||
/*
|
||||
** Provide the ability to override linkage features of the interface.
|
||||
** Facilitate override of interface linkage and calling conventions.
|
||||
** Be aware that these macros may not be used within this particular
|
||||
** translation of the amalgamation and its associated header file.
|
||||
**
|
||||
** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the
|
||||
** compiler that the target identifier should have external linkage.
|
||||
**
|
||||
** The SQLITE_CDECL macro is used to set the calling convention for
|
||||
** public functions that accept a variable number of arguments.
|
||||
**
|
||||
** The SQLITE_APICALL macro is used to set the calling convention for
|
||||
** public functions that accept a fixed number of arguments.
|
||||
**
|
||||
** The SQLITE_STDCALL macro is no longer used and is now deprecated.
|
||||
**
|
||||
** The SQLITE_CALLBACK macro is used to set the calling convention for
|
||||
** function pointers.
|
||||
**
|
||||
** The SQLITE_SYSAPI macro is used to set the calling convention for
|
||||
** functions provided by the operating system.
|
||||
**
|
||||
** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and
|
||||
** SQLITE_SYSAPI macros are used only when building for environments
|
||||
** that require non-default calling conventions.
|
||||
*/
|
||||
#ifndef SQLITE_EXTERN
|
||||
# define SQLITE_EXTERN extern
|
||||
|
@ -123,9 +146,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.35.4"
|
||||
#define SQLITE_VERSION_NUMBER 3035004
|
||||
#define SQLITE_SOURCE_ID "2021-04-02 15:20:15 5d4c65779dab868b285519b19e4cf9d451d50c6048f06f653aa701ec212df45e"
|
||||
#define SQLITE_VERSION "3.39.3"
|
||||
#define SQLITE_VERSION_NUMBER 3039003
|
||||
#define SQLITE_SOURCE_ID "2022-09-05 11:02:23 4635f4a69c8c2a8df242b384a992aea71224e39a2ccab42d8c0b0602f1e826e8"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -537,12 +560,13 @@ SQLITE_API int sqlite3_exec(
|
|||
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
|
||||
#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
|
||||
#define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8))
|
||||
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
|
||||
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
||||
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
||||
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
||||
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
|
||||
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
|
||||
#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8))
|
||||
#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags For File Open Operations
|
||||
|
@ -550,6 +574,19 @@ SQLITE_API int sqlite3_exec(
|
|||
** These bit values are intended for use in the
|
||||
** 3rd parameter to the [sqlite3_open_v2()] interface and
|
||||
** in the 4th parameter to the [sqlite3_vfs.xOpen] method.
|
||||
**
|
||||
** Only those flags marked as "Ok for sqlite3_open_v2()" may be
|
||||
** used as the third argument to the [sqlite3_open_v2()] interface.
|
||||
** The other flags have historically been ignored by sqlite3_open_v2(),
|
||||
** though future versions of SQLite might change so that an error is
|
||||
** raised if any of the disallowed bits are passed into sqlite3_open_v2().
|
||||
** Applications should not depend on the historical behavior.
|
||||
**
|
||||
** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
|
||||
** [sqlite3_open_v2()] does *not* cause the underlying database file
|
||||
** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
|
||||
** [sqlite3_open_v2()] has historically be a no-op and might become an
|
||||
** error in future versions of SQLite.
|
||||
*/
|
||||
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
|
||||
|
@ -572,6 +609,7 @@ SQLITE_API int sqlite3_exec(
|
|||
#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */
|
||||
#define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */
|
||||
|
||||
/* Reserved: 0x00F00000 */
|
||||
/* Legacy compatibility: */
|
||||
|
@ -1128,6 +1166,23 @@ struct sqlite3_io_methods {
|
|||
** file to the database file, but before the *-shm file is updated to
|
||||
** record the fact that the pages have been checkpointed.
|
||||
** </ul>
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
|
||||
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
|
||||
** whether or not there is a database client in another process with a wal-mode
|
||||
** transaction open on the database or not. It is only available on unix.The
|
||||
** (void*) argument passed with this file-control should be a pointer to a
|
||||
** value of type (int). The integer value is set to 1 if the database is a wal
|
||||
** mode database and there exists at least one client in another process that
|
||||
** currently has an SQL transaction open on the database. It is set to 0 if
|
||||
** the database is not a wal-mode db, or if there is no such connection in any
|
||||
** other process. This opcode cannot be used to detect transactions opened
|
||||
** by clients within the current process, only within other processes.
|
||||
** </ul>
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_CKSM_FILE]]
|
||||
** Used by the cksmvfs VFS module only.
|
||||
** </ul>
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2
|
||||
|
@ -1167,6 +1222,8 @@ struct sqlite3_io_methods {
|
|||
#define SQLITE_FCNTL_CKPT_DONE 37
|
||||
#define SQLITE_FCNTL_RESERVE_BYTES 38
|
||||
#define SQLITE_FCNTL_CKPT_START 39
|
||||
#define SQLITE_FCNTL_EXTERNAL_READER 40
|
||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
|
@ -2445,11 +2502,14 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
|
|||
** CAPI3REF: Count The Number Of Rows Modified
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^This function returns the number of rows modified, inserted or
|
||||
** ^These functions return the number of rows modified, inserted or
|
||||
** deleted by the most recently completed INSERT, UPDATE or DELETE
|
||||
** statement on the database connection specified by the only parameter.
|
||||
** ^Executing any other type of SQL statement does not modify the value
|
||||
** returned by this function.
|
||||
** The two functions are identical except for the type of the return value
|
||||
** and that if the number of rows modified by the most recent INSERT, UPDATE
|
||||
** or DELETE is greater than the maximum value supported by type "int", then
|
||||
** the return value of sqlite3_changes() is undefined. ^Executing any other
|
||||
** type of SQL statement does not modify the value returned by these functions.
|
||||
**
|
||||
** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
|
||||
** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
|
||||
|
@ -2498,16 +2558,21 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
|
|||
** </ul>
|
||||
*/
|
||||
SQLITE_API int sqlite3_changes(sqlite3*);
|
||||
SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Total Number Of Rows Modified
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^This function returns the total number of rows inserted, modified or
|
||||
** ^These functions return the total number of rows inserted, modified or
|
||||
** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
|
||||
** since the database connection was opened, including those executed as
|
||||
** part of trigger programs. ^Executing any other type of SQL statement
|
||||
** does not affect the value returned by sqlite3_total_changes().
|
||||
** part of trigger programs. The two functions are identical except for the
|
||||
** type of the return value and that if the number of rows modified by the
|
||||
** connection exceeds the maximum value supported by type "int", then
|
||||
** the return value of sqlite3_total_changes() is undefined. ^Executing
|
||||
** any other type of SQL statement does not affect the value returned by
|
||||
** sqlite3_total_changes().
|
||||
**
|
||||
** ^Changes made as part of [foreign key actions] are included in the
|
||||
** count, but those made as part of REPLACE constraint resolution are
|
||||
|
@ -2535,6 +2600,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
|
|||
** </ul>
|
||||
*/
|
||||
SQLITE_API int sqlite3_total_changes(sqlite3*);
|
||||
SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Interrupt A Long-Running Query
|
||||
|
@ -3364,6 +3430,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||
** the default shared cache setting provided by
|
||||
** [sqlite3_enable_shared_cache()].)^
|
||||
**
|
||||
** [[OPEN_EXRESCODE]] ^(<dt>[SQLITE_OPEN_EXRESCODE]</dt>
|
||||
** <dd>The database connection comes up in "extended result code mode".
|
||||
** In other words, the database behaves has if
|
||||
** [sqlite3_extended_result_codes(db,1)] where called on the database
|
||||
** connection as soon as the connection is created. In addition to setting
|
||||
** the extended result code mode, this flag also causes [sqlite3_open_v2()]
|
||||
** to return an extended result code.</dd>
|
||||
**
|
||||
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
|
||||
** <dd>The database filename is not allowed to be a symbolic link</dd>
|
||||
** </dl>)^
|
||||
|
@ -3371,7 +3445,15 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||
** If the 3rd parameter to sqlite3_open_v2() is not one of the
|
||||
** required combinations shown above optionally combined with other
|
||||
** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]
|
||||
** then the behavior is undefined.
|
||||
** then the behavior is undefined. Historic versions of SQLite
|
||||
** have silently ignored surplus bits in the flags parameter to
|
||||
** sqlite3_open_v2(), however that behavior might not be carried through
|
||||
** into future versions of SQLite and so applications should not rely
|
||||
** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op
|
||||
** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause
|
||||
** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE
|
||||
** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not
|
||||
** by sqlite3_open_v2().
|
||||
**
|
||||
** ^The fourth parameter to sqlite3_open_v2() is the name of the
|
||||
** [sqlite3_vfs] object that defines the operating system interface that
|
||||
|
@ -3742,13 +3824,14 @@ SQLITE_API void sqlite3_free_filename(char*);
|
|||
** sqlite3_extended_errcode() might change with each API call.
|
||||
** Except, there are some interfaces that are guaranteed to never
|
||||
** change the value of the error code. The error-code preserving
|
||||
** interfaces are:
|
||||
** interfaces include the following:
|
||||
**
|
||||
** <ul>
|
||||
** <li> sqlite3_errcode()
|
||||
** <li> sqlite3_extended_errcode()
|
||||
** <li> sqlite3_errmsg()
|
||||
** <li> sqlite3_errmsg16()
|
||||
** <li> sqlite3_error_offset()
|
||||
** </ul>
|
||||
**
|
||||
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
|
||||
|
@ -3763,6 +3846,13 @@ SQLITE_API void sqlite3_free_filename(char*);
|
|||
** ^(Memory to hold the error message string is managed internally
|
||||
** and must not be freed by the application)^.
|
||||
**
|
||||
** ^If the most recent error references a specific token in the input
|
||||
** SQL, the sqlite3_error_offset() interface returns the byte offset
|
||||
** of the start of that token. ^The byte offset returned by
|
||||
** sqlite3_error_offset() assumes that the input SQL is UTF8.
|
||||
** ^If the most recent error does not reference a specific token in the input
|
||||
** SQL, then the sqlite3_error_offset() function returns -1.
|
||||
**
|
||||
** When the serialized [threading mode] is in use, it might be the
|
||||
** case that a second error occurs on a separate thread in between
|
||||
** the time of the first error and the call to these interfaces.
|
||||
|
@ -3782,6 +3872,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
|
|||
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
|
||||
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
|
||||
SQLITE_API const char *sqlite3_errstr(int);
|
||||
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Prepared Statement Object
|
||||
|
@ -4139,12 +4230,17 @@ SQLITE_API int sqlite3_prepare16_v3(
|
|||
** are managed by SQLite and are automatically freed when the prepared
|
||||
** statement is finalized.
|
||||
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
|
||||
** is obtained from [sqlite3_malloc()] and must be free by the application
|
||||
** is obtained from [sqlite3_malloc()] and must be freed by the application
|
||||
** by passing it to [sqlite3_free()].
|
||||
**
|
||||
** ^The sqlite3_normalized_sql() interface is only available if
|
||||
** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined.
|
||||
*/
|
||||
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
|
||||
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
|
||||
#ifdef SQLITE_ENABLE_NORMALIZE
|
||||
SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine If An SQL Statement Writes The Database
|
||||
|
@ -4179,6 +4275,19 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
|
|||
** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and
|
||||
** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so
|
||||
** sqlite3_stmt_readonly() returns false for those commands.
|
||||
**
|
||||
** ^This routine returns false if there is any possibility that the
|
||||
** statement might change the database file. ^A false return does
|
||||
** not guarantee that the statement will change the database file.
|
||||
** ^For example, an UPDATE statement might have a WHERE clause that
|
||||
** makes it a no-op, but the sqlite3_stmt_readonly() result would still
|
||||
** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a
|
||||
** read-only no-op if the table already exists, but
|
||||
** sqlite3_stmt_readonly() still returns false for such a statement.
|
||||
**
|
||||
** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN]
|
||||
** statement, then sqlite3_stmt_readonly(X) returns the same value as
|
||||
** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted.
|
||||
*/
|
||||
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
|
||||
|
||||
|
@ -4247,6 +4356,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
|
|||
**
|
||||
** ^The sqlite3_value objects that are passed as parameters into the
|
||||
** implementation of [application-defined SQL functions] are protected.
|
||||
** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()]
|
||||
** are protected.
|
||||
** ^The sqlite3_value object returned by
|
||||
** [sqlite3_column_value()] is unprotected.
|
||||
** Unprotected sqlite3_value objects may only be used as arguments
|
||||
|
@ -4348,18 +4459,22 @@ typedef struct sqlite3_context sqlite3_context;
|
|||
** contain embedded NULs. The result of expressions involving strings
|
||||
** with embedded NULs is undefined.
|
||||
**
|
||||
** ^The fifth argument to the BLOB and string binding interfaces
|
||||
** is a destructor used to dispose of the BLOB or
|
||||
** string after SQLite has finished with it. ^The destructor is called
|
||||
** to dispose of the BLOB or string even if the call to the bind API fails,
|
||||
** except the destructor is not called if the third parameter is a NULL
|
||||
** pointer or the fourth parameter is negative.
|
||||
** ^If the fifth argument is
|
||||
** the special value [SQLITE_STATIC], then SQLite assumes that the
|
||||
** information is in static, unmanaged space and does not need to be freed.
|
||||
** ^If the fifth argument has the value [SQLITE_TRANSIENT], then
|
||||
** SQLite makes its own private copy of the data immediately, before
|
||||
** the sqlite3_bind_*() routine returns.
|
||||
** ^The fifth argument to the BLOB and string binding interfaces controls
|
||||
** or indicates the lifetime of the object referenced by the third parameter.
|
||||
** These three options exist:
|
||||
** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished
|
||||
** with it may be passed. ^It is called to dispose of the BLOB or string even
|
||||
** if the call to the bind API fails, except the destructor is not called if
|
||||
** the third parameter is a NULL pointer or the fourth parameter is negative.
|
||||
** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that
|
||||
** the application remains responsible for disposing of the object. ^In this
|
||||
** case, the object and the provided pointer to it must remain valid until
|
||||
** either the prepared statement is finalized or the same SQL parameter is
|
||||
** bound to something else, whichever occurs sooner.
|
||||
** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the
|
||||
** object is to be copied prior to the return from sqlite3_bind_*(). ^The
|
||||
** object and pointer to it must remain valid until then. ^SQLite will then
|
||||
** manage the lifetime of its private copy.
|
||||
**
|
||||
** ^The sixth argument to sqlite3_bind_text64() must be one of
|
||||
** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
|
||||
|
@ -4864,6 +4979,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
|
|||
** even empty strings, are always zero-terminated. ^The return
|
||||
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
|
||||
**
|
||||
** ^Strings returned by sqlite3_column_text16() always have the endianness
|
||||
** which is native to the platform, regardless of the text encoding set
|
||||
** for the database.
|
||||
**
|
||||
** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
|
||||
** [unprotected sqlite3_value] object. In a multithreaded environment,
|
||||
** an unprotected sqlite3_value object may only be used safely with
|
||||
|
@ -4877,7 +4996,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
|
|||
** [application-defined SQL functions] or [virtual tables], not within
|
||||
** top-level application code.
|
||||
**
|
||||
** The these routines may attempt to convert the datatype of the result.
|
||||
** These routines may attempt to convert the datatype of the result.
|
||||
** ^For example, if the internal representation is FLOAT and a text result
|
||||
** is requested, [sqlite3_snprintf()] is used internally to perform the
|
||||
** conversion automatically. ^(The following table details the conversions
|
||||
|
@ -4902,7 +5021,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
|
|||
** <tr><td> TEXT <td> BLOB <td> No change
|
||||
** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER
|
||||
** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
|
||||
** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
|
||||
** <tr><td> BLOB <td> TEXT <td> [CAST] to TEXT, ensure zero terminator
|
||||
** </table>
|
||||
** </blockquote>)^
|
||||
**
|
||||
|
@ -5101,7 +5220,6 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
|
|||
** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions,
|
||||
** index expressions, or the WHERE clause of partial indexes.
|
||||
**
|
||||
** <span style="background-color:#ffff90;">
|
||||
** For best security, the [SQLITE_DIRECTONLY] flag is recommended for
|
||||
** all application-defined SQL functions that do not need to be
|
||||
** used inside of triggers, view, CHECK constraints, or other elements of
|
||||
|
@ -5111,7 +5229,6 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
|
|||
** a database file to include invocations of the function with parameters
|
||||
** chosen by the attacker, which the application will then execute when
|
||||
** the database file is opened and read.
|
||||
** </span>
|
||||
**
|
||||
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
|
||||
** function can gain access to this pointer using [sqlite3_user_data()].)^
|
||||
|
@ -5476,7 +5593,8 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
|
|||
** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
|
||||
** is a [protected sqlite3_value] object even if the input is not.
|
||||
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
|
||||
** memory allocation fails.
|
||||
** memory allocation fails. ^If V is a [pointer value], then the result
|
||||
** of sqlite3_value_dup(V) is a NULL value.
|
||||
**
|
||||
** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
|
||||
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
|
||||
|
@ -6158,6 +6276,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
|
|||
*/
|
||||
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return The Schema Name For A Database Connection
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
|
||||
** for the N-th database on database connection D, or a NULL pointer of N is
|
||||
** out of range. An N value of 0 means the main database file. An N of 1 is
|
||||
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
|
||||
** databases.
|
||||
**
|
||||
** Space to hold the string that is returned by sqlite3_db_name() is managed
|
||||
** by SQLite itself. The string might be deallocated by any operation that
|
||||
** changes the schema, including [ATTACH] or [DETACH] or calls to
|
||||
** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that
|
||||
** occur on a different thread. Applications that need to
|
||||
** remember the string long-term should make their own copy. Applications that
|
||||
** are accessing the same database connection simultaneously on multiple
|
||||
** threads should mutex-protect calls to this API and should make their own
|
||||
** private copy of the result prior to releasing the mutex.
|
||||
*/
|
||||
SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return The Filename For A Database Connection
|
||||
** METHOD: sqlite3
|
||||
|
@ -6317,6 +6457,72 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
|
|||
SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
|
||||
SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Autovacuum Compaction Amount Callback
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback
|
||||
** function C that is invoked prior to each autovacuum of the database
|
||||
** file. ^The callback is passed a copy of the generic data pointer (P),
|
||||
** the schema-name of the attached database that is being autovacuumed,
|
||||
** the the size of the database file in pages, the number of free pages,
|
||||
** and the number of bytes per page, respectively. The callback should
|
||||
** return the number of free pages that should be removed by the
|
||||
** autovacuum. ^If the callback returns zero, then no autovacuum happens.
|
||||
** ^If the value returned is greater than or equal to the number of
|
||||
** free pages, then a complete autovacuum happens.
|
||||
**
|
||||
** <p>^If there are multiple ATTACH-ed database files that are being
|
||||
** modified as part of a transaction commit, then the autovacuum pages
|
||||
** callback is invoked separately for each file.
|
||||
**
|
||||
** <p><b>The callback is not reentrant.</b> The callback function should
|
||||
** not attempt to invoke any other SQLite interface. If it does, bad
|
||||
** things may happen, including segmentation faults and corrupt database
|
||||
** files. The callback function should be a simple function that
|
||||
** does some arithmetic on its input parameters and returns a result.
|
||||
**
|
||||
** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional
|
||||
** destructor for the P parameter. ^If X is not NULL, then X(P) is
|
||||
** invoked whenever the database connection closes or when the callback
|
||||
** is overwritten by another invocation of sqlite3_autovacuum_pages().
|
||||
**
|
||||
** <p>^There is only one autovacuum pages callback per database connection.
|
||||
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
|
||||
** previous invocations for that database connection. ^If the callback
|
||||
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
|
||||
** then the autovacuum steps callback is cancelled. The return value
|
||||
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
|
||||
** be some other error code if something goes wrong. The current
|
||||
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
|
||||
** return codes might be added in future releases.
|
||||
**
|
||||
** <p>If no autovacuum pages callback is specified (the usual case) or
|
||||
** a NULL pointer is provided for the callback,
|
||||
** then the default behavior is to vacuum all free pages. So, in other
|
||||
** words, the default behavior is the same as if the callback function
|
||||
** were something like this:
|
||||
**
|
||||
** <blockquote><pre>
|
||||
** unsigned int demonstration_autovac_pages_callback(
|
||||
** void *pClientData,
|
||||
** const char *zSchema,
|
||||
** unsigned int nDbPage,
|
||||
** unsigned int nFreePage,
|
||||
** unsigned int nBytePerPage
|
||||
** ){
|
||||
** return nFreePage;
|
||||
** }
|
||||
** </pre></blockquote>
|
||||
*/
|
||||
SQLITE_API int sqlite3_autovacuum_pages(
|
||||
sqlite3 *db,
|
||||
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
|
||||
void*,
|
||||
void(*)(void*)
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Data Change Notification Callbacks
|
||||
** METHOD: sqlite3
|
||||
|
@ -6958,8 +7164,38 @@ struct sqlite3_index_info {
|
|||
**
|
||||
** These macros define the allowed values for the
|
||||
** [sqlite3_index_info].aConstraint[].op field. Each value represents
|
||||
** an operator that is part of a constraint term in the wHERE clause of
|
||||
** an operator that is part of a constraint term in the WHERE clause of
|
||||
** a query that uses a [virtual table].
|
||||
**
|
||||
** ^The left-hand operand of the operator is given by the corresponding
|
||||
** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand
|
||||
** operand is the rowid.
|
||||
** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
** operators have no left-hand operand, and so for those operators the
|
||||
** corresponding aConstraint[].iColumn is meaningless and should not be
|
||||
** used.
|
||||
**
|
||||
** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
|
||||
** value 255 are reserved to represent functions that are overloaded
|
||||
** by the [xFindFunction|xFindFunction method] of the virtual table
|
||||
** implementation.
|
||||
**
|
||||
** The right-hand operands for each constraint might be accessible using
|
||||
** the [sqlite3_vtab_rhs_value()] interface. Usually the right-hand
|
||||
** operand is only available if it appears as a single constant literal
|
||||
** in the input SQL. If the right-hand operand is another column or an
|
||||
** expression (even a constant expression) or a parameter, then the
|
||||
** sqlite3_vtab_rhs_value() probably will not be able to extract it.
|
||||
** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
|
||||
** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
|
||||
** and hence calls to sqlite3_vtab_rhs_value() for those operators will
|
||||
** always return SQLITE_NOTFOUND.
|
||||
**
|
||||
** The collating sequence to be used for comparison can be found using
|
||||
** the [sqlite3_vtab_collation()] interface. For most real-world virtual
|
||||
** tables, the collating sequence of constraints does not matter (for example
|
||||
** because the constraints are numeric) and so the sqlite3_vtab_collation()
|
||||
** interface is no commonly needed.
|
||||
*/
|
||||
#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
||||
#define SQLITE_INDEX_CONSTRAINT_GT 4
|
||||
|
@ -6975,6 +7211,8 @@ struct sqlite3_index_info {
|
|||
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
|
||||
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
|
||||
#define SQLITE_INDEX_CONSTRAINT_IS 72
|
||||
#define SQLITE_INDEX_CONSTRAINT_LIMIT 73
|
||||
#define SQLITE_INDEX_CONSTRAINT_OFFSET 74
|
||||
#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
|
||||
|
||||
/*
|
||||
|
@ -7004,7 +7242,7 @@ struct sqlite3_index_info {
|
|||
** destructor.
|
||||
**
|
||||
** ^If the third parameter (the pointer to the sqlite3_module object) is
|
||||
** NULL then no new module is create and any existing modules with the
|
||||
** NULL then no new module is created and any existing modules with the
|
||||
** same name are dropped.
|
||||
**
|
||||
** See also: [sqlite3_drop_modules()]
|
||||
|
@ -7779,7 +8017,9 @@ SQLITE_API int sqlite3_test_control(int op, ...);
|
|||
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
|
||||
#define SQLITE_TESTCTRL_SEEK_COUNT 30
|
||||
#define SQLITE_TESTCTRL_TRACEFLAGS 31
|
||||
#define SQLITE_TESTCTRL_LAST 31 /* Largest TESTCTRL */
|
||||
#define SQLITE_TESTCTRL_TUNE 32
|
||||
#define SQLITE_TESTCTRL_LOGEST 33
|
||||
#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Keyword Checking
|
||||
|
@ -8302,6 +8542,16 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
|||
** The counter is incremented on the first [sqlite3_step()] call of each
|
||||
** cycle.
|
||||
**
|
||||
** [[SQLITE_STMTSTATUS_FILTER_MISS]]
|
||||
** [[SQLITE_STMTSTATUS_FILTER HIT]]
|
||||
** <dt>SQLITE_STMTSTATUS_FILTER_HIT<br>
|
||||
** SQLITE_STMTSTATUS_FILTER_MISS</dt>
|
||||
** <dd>^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join
|
||||
** step was bypassed because a Bloom filter returned not-found. The
|
||||
** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of
|
||||
** times that the Bloom filter returned a find, and thus the join step
|
||||
** had to be processed as normal.
|
||||
**
|
||||
** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt>
|
||||
** <dd>^This is the approximate number of bytes of heap memory
|
||||
** used to store the prepared statement. ^This value is not actually
|
||||
|
@ -8316,6 +8566,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
|||
#define SQLITE_STMTSTATUS_VM_STEP 4
|
||||
#define SQLITE_STMTSTATUS_REPREPARE 5
|
||||
#define SQLITE_STMTSTATUS_RUN 6
|
||||
#define SQLITE_STMTSTATUS_FILTER_MISS 7
|
||||
#define SQLITE_STMTSTATUS_FILTER_HIT 8
|
||||
#define SQLITE_STMTSTATUS_MEMUSED 99
|
||||
|
||||
/*
|
||||
|
@ -8979,8 +9231,9 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
|
|||
**
|
||||
** A single database handle may have at most a single write-ahead log callback
|
||||
** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
|
||||
** previously registered write-ahead log callback. ^Note that the
|
||||
** [sqlite3_wal_autocheckpoint()] interface and the
|
||||
** previously registered write-ahead log callback. ^The return value is
|
||||
** a copy of the third parameter from the previous call, if any, or 0.
|
||||
** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
|
||||
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
|
||||
** overwrite any prior [sqlite3_wal_hook()] settings.
|
||||
*/
|
||||
|
@ -9283,19 +9536,276 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
|
|||
|
||||
/*
|
||||
** CAPI3REF: Determine The Collation For a Virtual Table Constraint
|
||||
** METHOD: sqlite3_index_info
|
||||
**
|
||||
** This function may only be called from within a call to the [xBestIndex]
|
||||
** method of a [virtual table].
|
||||
** method of a [virtual table]. This function returns a pointer to a string
|
||||
** that is the name of the appropriate collation sequence to use for text
|
||||
** comparisons on the constraint identified by its arguments.
|
||||
**
|
||||
** The first argument must be the sqlite3_index_info object that is the
|
||||
** first parameter to the xBestIndex() method. The second argument must be
|
||||
** an index into the aConstraint[] array belonging to the sqlite3_index_info
|
||||
** structure passed to xBestIndex. This function returns a pointer to a buffer
|
||||
** containing the name of the collation sequence for the corresponding
|
||||
** constraint.
|
||||
** The first argument must be the pointer to the [sqlite3_index_info] object
|
||||
** that is the first parameter to the xBestIndex() method. The second argument
|
||||
** must be an index into the aConstraint[] array belonging to the
|
||||
** sqlite3_index_info structure passed to xBestIndex.
|
||||
**
|
||||
** Important:
|
||||
** The first parameter must be the same pointer that is passed into the
|
||||
** xBestMethod() method. The first parameter may not be a pointer to a
|
||||
** different [sqlite3_index_info] object, even an exact copy.
|
||||
**
|
||||
** The return value is computed as follows:
|
||||
**
|
||||
** <ol>
|
||||
** <li><p> If the constraint comes from a WHERE clause expression that contains
|
||||
** a [COLLATE operator], then the name of the collation specified by
|
||||
** that COLLATE operator is returned.
|
||||
** <li><p> If there is no COLLATE operator, but the column that is the subject
|
||||
** of the constraint specifies an alternative collating sequence via
|
||||
** a [COLLATE clause] on the column definition within the CREATE TABLE
|
||||
** statement that was passed into [sqlite3_declare_vtab()], then the
|
||||
** name of that alternative collating sequence is returned.
|
||||
** <li><p> Otherwise, "BINARY" is returned.
|
||||
** </ol>
|
||||
*/
|
||||
SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine if a virtual table query is DISTINCT
|
||||
** METHOD: sqlite3_index_info
|
||||
**
|
||||
** This API may only be used from within an [xBestIndex|xBestIndex method]
|
||||
** of a [virtual table] implementation. The result of calling this
|
||||
** interface from outside of xBestIndex() is undefined and probably harmful.
|
||||
**
|
||||
** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and
|
||||
** 3. The integer returned by sqlite3_vtab_distinct()
|
||||
** gives the virtual table additional information about how the query
|
||||
** planner wants the output to be ordered. As long as the virtual table
|
||||
** can meet the ordering requirements of the query planner, it may set
|
||||
** the "orderByConsumed" flag.
|
||||
**
|
||||
** <ol><li value="0"><p>
|
||||
** ^If the sqlite3_vtab_distinct() interface returns 0, that means
|
||||
** that the query planner needs the virtual table to return all rows in the
|
||||
** sort order defined by the "nOrderBy" and "aOrderBy" fields of the
|
||||
** [sqlite3_index_info] object. This is the default expectation. If the
|
||||
** virtual table outputs all rows in sorted order, then it is always safe for
|
||||
** the xBestIndex method to set the "orderByConsumed" flag, regardless of
|
||||
** the return value from sqlite3_vtab_distinct().
|
||||
** <li value="1"><p>
|
||||
** ^(If the sqlite3_vtab_distinct() interface returns 1, that means
|
||||
** that the query planner does not need the rows to be returned in sorted order
|
||||
** as long as all rows with the same values in all columns identified by the
|
||||
** "aOrderBy" field are adjacent.)^ This mode is used when the query planner
|
||||
** is doing a GROUP BY.
|
||||
** <li value="2"><p>
|
||||
** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
|
||||
** that the query planner does not need the rows returned in any particular
|
||||
** order, as long as rows with the same values in all "aOrderBy" columns
|
||||
** are adjacent.)^ ^(Furthermore, only a single row for each particular
|
||||
** combination of values in the columns identified by the "aOrderBy" field
|
||||
** needs to be returned.)^ ^It is always ok for two or more rows with the same
|
||||
** values in all "aOrderBy" columns to be returned, as long as all such rows
|
||||
** are adjacent. ^The virtual table may, if it chooses, omit extra rows
|
||||
** that have the same value for all columns identified by "aOrderBy".
|
||||
** ^However omitting the extra rows is optional.
|
||||
** This mode is used for a DISTINCT query.
|
||||
** <li value="3"><p>
|
||||
** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
|
||||
** that the query planner needs only distinct rows but it does need the
|
||||
** rows to be sorted.)^ ^The virtual table implementation is free to omit
|
||||
** rows that are identical in all aOrderBy columns, if it wants to, but
|
||||
** it is not required to omit any rows. This mode is used for queries
|
||||
** that have both DISTINCT and ORDER BY clauses.
|
||||
** </ol>
|
||||
**
|
||||
** ^For the purposes of comparing virtual table output values to see if the
|
||||
** values are same value for sorting purposes, two NULL values are considered
|
||||
** to be the same. In other words, the comparison operator is "IS"
|
||||
** (or "IS NOT DISTINCT FROM") and not "==".
|
||||
**
|
||||
** If a virtual table implementation is unable to meet the requirements
|
||||
** specified above, then it must not set the "orderByConsumed" flag in the
|
||||
** [sqlite3_index_info] object or an incorrect answer may result.
|
||||
**
|
||||
** ^A virtual table implementation is always free to return rows in any order
|
||||
** it wants, as long as the "orderByConsumed" flag is not set. ^When the
|
||||
** the "orderByConsumed" flag is unset, the query planner will add extra
|
||||
** [bytecode] to ensure that the final results returned by the SQL query are
|
||||
** ordered correctly. The use of the "orderByConsumed" flag and the
|
||||
** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful
|
||||
** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed"
|
||||
** flag might help queries against a virtual table to run faster. Being
|
||||
** overly aggressive and setting the "orderByConsumed" flag when it is not
|
||||
** valid to do so, on the other hand, might cause SQLite to return incorrect
|
||||
** results.
|
||||
*/
|
||||
SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Identify and handle IN constraints in xBestIndex
|
||||
**
|
||||
** This interface may only be used from within an
|
||||
** [xBestIndex|xBestIndex() method] of a [virtual table] implementation.
|
||||
** The result of invoking this interface from any other context is
|
||||
** undefined and probably harmful.
|
||||
**
|
||||
** ^(A constraint on a virtual table of the form
|
||||
** "[IN operator|column IN (...)]" is
|
||||
** communicated to the xBestIndex method as a
|
||||
** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use
|
||||
** this constraint, it must set the corresponding
|
||||
** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under
|
||||
** the usual mode of handling IN operators, SQLite generates [bytecode]
|
||||
** that invokes the [xFilter|xFilter() method] once for each value
|
||||
** on the right-hand side of the IN operator.)^ Thus the virtual table
|
||||
** only sees a single value from the right-hand side of the IN operator
|
||||
** at a time.
|
||||
**
|
||||
** In some cases, however, it would be advantageous for the virtual
|
||||
** table to see all values on the right-hand of the IN operator all at
|
||||
** once. The sqlite3_vtab_in() interfaces facilitates this in two ways:
|
||||
**
|
||||
** <ol>
|
||||
** <li><p>
|
||||
** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero)
|
||||
** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint
|
||||
** is an [IN operator] that can be processed all at once. ^In other words,
|
||||
** sqlite3_vtab_in() with -1 in the third argument is a mechanism
|
||||
** by which the virtual table can ask SQLite if all-at-once processing
|
||||
** of the IN operator is even possible.
|
||||
**
|
||||
** <li><p>
|
||||
** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates
|
||||
** to SQLite that the virtual table does or does not want to process
|
||||
** the IN operator all-at-once, respectively. ^Thus when the third
|
||||
** parameter (F) is non-negative, this interface is the mechanism by
|
||||
** which the virtual table tells SQLite how it wants to process the
|
||||
** IN operator.
|
||||
** </ol>
|
||||
**
|
||||
** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times
|
||||
** within the same xBestIndex method call. ^For any given P,N pair,
|
||||
** the return value from sqlite3_vtab_in(P,N,F) will always be the same
|
||||
** within the same xBestIndex call. ^If the interface returns true
|
||||
** (non-zero), that means that the constraint is an IN operator
|
||||
** that can be processed all-at-once. ^If the constraint is not an IN
|
||||
** operator or cannot be processed all-at-once, then the interface returns
|
||||
** false.
|
||||
**
|
||||
** ^(All-at-once processing of the IN operator is selected if both of the
|
||||
** following conditions are met:
|
||||
**
|
||||
** <ol>
|
||||
** <li><p> The P->aConstraintUsage[N].argvIndex value is set to a positive
|
||||
** integer. This is how the virtual table tells SQLite that it wants to
|
||||
** use the N-th constraint.
|
||||
**
|
||||
** <li><p> The last call to sqlite3_vtab_in(P,N,F) for which F was
|
||||
** non-negative had F>=1.
|
||||
** </ol>)^
|
||||
**
|
||||
** ^If either or both of the conditions above are false, then SQLite uses
|
||||
** the traditional one-at-a-time processing strategy for the IN constraint.
|
||||
** ^If both conditions are true, then the argvIndex-th parameter to the
|
||||
** xFilter method will be an [sqlite3_value] that appears to be NULL,
|
||||
** but which can be passed to [sqlite3_vtab_in_first()] and
|
||||
** [sqlite3_vtab_in_next()] to find all values on the right-hand side
|
||||
** of the IN constraint.
|
||||
*/
|
||||
SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Find all elements on the right-hand side of an IN constraint.
|
||||
**
|
||||
** These interfaces are only useful from within the
|
||||
** [xFilter|xFilter() method] of a [virtual table] implementation.
|
||||
** The result of invoking these interfaces from any other context
|
||||
** is undefined and probably harmful.
|
||||
**
|
||||
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
|
||||
** sqlite3_vtab_in_next(X,P) must be one of the parameters to the
|
||||
** xFilter method which invokes these routines, and specifically
|
||||
** a parameter that was previously selected for all-at-once IN constraint
|
||||
** processing use the [sqlite3_vtab_in()] interface in the
|
||||
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
|
||||
** an xFilter argument that was selected for all-at-once IN constraint
|
||||
** processing, then these routines return [SQLITE_MISUSE])^ or perhaps
|
||||
** exhibit some other undefined or harmful behavior.
|
||||
**
|
||||
** ^(Use these routines to access all values on the right-hand side
|
||||
** of the IN constraint using code like the following:
|
||||
**
|
||||
** <blockquote><pre>
|
||||
** for(rc=sqlite3_vtab_in_first(pList, &pVal);
|
||||
** rc==SQLITE_OK && pVal
|
||||
** rc=sqlite3_vtab_in_next(pList, &pVal)
|
||||
** ){
|
||||
** // do something with pVal
|
||||
** }
|
||||
** if( rc!=SQLITE_OK ){
|
||||
** // an error has occurred
|
||||
** }
|
||||
** </pre></blockquote>)^
|
||||
**
|
||||
** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P)
|
||||
** routines return SQLITE_OK and set *P to point to the first or next value
|
||||
** on the RHS of the IN constraint. ^If there are no more values on the
|
||||
** right hand side of the IN constraint, then *P is set to NULL and these
|
||||
** routines return [SQLITE_DONE]. ^The return value might be
|
||||
** some other value, such as SQLITE_NOMEM, in the event of a malfunction.
|
||||
**
|
||||
** The *ppOut values returned by these routines are only valid until the
|
||||
** next call to either of these routines or until the end of the xFilter
|
||||
** method from which these routines were called. If the virtual table
|
||||
** implementation needs to retain the *ppOut values for longer, it must make
|
||||
** copies. The *ppOut values are [protected sqlite3_value|protected].
|
||||
*/
|
||||
SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut);
|
||||
SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Constraint values in xBestIndex()
|
||||
** METHOD: sqlite3_index_info
|
||||
**
|
||||
** This API may only be used from within the [xBestIndex|xBestIndex method]
|
||||
** of a [virtual table] implementation. The result of calling this interface
|
||||
** from outside of an xBestIndex method are undefined and probably harmful.
|
||||
**
|
||||
** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within
|
||||
** the [xBestIndex] method of a [virtual table] implementation, with P being
|
||||
** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and
|
||||
** J being a 0-based index into P->aConstraint[], then this routine
|
||||
** attempts to set *V to the value of the right-hand operand of
|
||||
** that constraint if the right-hand operand is known. ^If the
|
||||
** right-hand operand is not known, then *V is set to a NULL pointer.
|
||||
** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if
|
||||
** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V)
|
||||
** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th
|
||||
** constraint is not available. ^The sqlite3_vtab_rhs_value() interface
|
||||
** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if
|
||||
** something goes wrong.
|
||||
**
|
||||
** The sqlite3_vtab_rhs_value() interface is usually only successful if
|
||||
** the right-hand operand of a constraint is a literal value in the original
|
||||
** SQL statement. If the right-hand operand is an expression or a reference
|
||||
** to some other column or a [host parameter], then sqlite3_vtab_rhs_value()
|
||||
** will probably return [SQLITE_NOTFOUND].
|
||||
**
|
||||
** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and
|
||||
** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such
|
||||
** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^
|
||||
**
|
||||
** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value
|
||||
** and remains valid for the duration of the xBestIndex method call.
|
||||
** ^When xBestIndex returns, the sqlite3_value object returned by
|
||||
** sqlite3_vtab_rhs_value() is automatically deallocated.
|
||||
**
|
||||
** The "_rhs_" in the name of this routine is an abbreviation for
|
||||
** "Right-Hand Side".
|
||||
*/
|
||||
SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Conflict resolution modes
|
||||
** KEYWORDS: {conflict resolution mode}
|
||||
|
@ -9531,6 +10041,15 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
|
|||
** triggers; or 2 for changes resulting from triggers called by top-level
|
||||
** triggers; and so forth.
|
||||
**
|
||||
** When the [sqlite3_blob_write()] API is used to update a blob column,
|
||||
** the pre-update hook is invoked with SQLITE_DELETE. This is because the
|
||||
** in this case the new values are not available. In this case, when a
|
||||
** callback made with op==SQLITE_DELETE is actuall a write using the
|
||||
** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns
|
||||
** the index of the column being written. In other cases, where the
|
||||
** pre-update hook is being invoked for some other reason, including a
|
||||
** regular DELETE, sqlite3_preupdate_blobwrite() returns -1.
|
||||
**
|
||||
** See also: [sqlite3_update_hook()]
|
||||
*/
|
||||
#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
||||
|
@ -9551,6 +10070,7 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
|
|||
SQLITE_API int sqlite3_preupdate_count(sqlite3 *);
|
||||
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *);
|
||||
SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
|
||||
SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -9789,8 +10309,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
|
|||
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
|
||||
** allocation error occurs.
|
||||
**
|
||||
** This interface is only available if SQLite is compiled with the
|
||||
** [SQLITE_ENABLE_DESERIALIZE] option.
|
||||
** This interface is omitted if SQLite is compiled with the
|
||||
** [SQLITE_OMIT_DESERIALIZE] option.
|
||||
*/
|
||||
SQLITE_API unsigned char *sqlite3_serialize(
|
||||
sqlite3 *db, /* The database connection */
|
||||
|
@ -9837,12 +10357,16 @@ SQLITE_API unsigned char *sqlite3_serialize(
|
|||
** database is currently in a read transaction or is involved in a backup
|
||||
** operation.
|
||||
**
|
||||
** It is not possible to deserialized into the TEMP database. If the
|
||||
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
|
||||
** function returns SQLITE_ERROR.
|
||||
**
|
||||
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
|
||||
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
|
||||
** [sqlite3_free()] is invoked on argument P prior to returning.
|
||||
**
|
||||
** This interface is only available if SQLite is compiled with the
|
||||
** [SQLITE_ENABLE_DESERIALIZE] option.
|
||||
** This interface is omitted if SQLite is compiled with the
|
||||
** [SQLITE_OMIT_DESERIALIZE] option.
|
||||
*/
|
||||
SQLITE_API int sqlite3_deserialize(
|
||||
sqlite3 *db, /* The database connection */
|
||||
|
@ -10091,6 +10615,38 @@ SQLITE_API int sqlite3session_create(
|
|||
*/
|
||||
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
|
||||
|
||||
/*
|
||||
** CAPIREF: Conigure a Session Object
|
||||
** METHOD: sqlite3_session
|
||||
**
|
||||
** This method is used to configure a session object after it has been
|
||||
** created. At present the only valid value for the second parameter is
|
||||
** [SQLITE_SESSION_OBJCONFIG_SIZE].
|
||||
**
|
||||
** Arguments for sqlite3session_object_config()
|
||||
**
|
||||
** The following values may passed as the the 4th parameter to
|
||||
** sqlite3session_object_config().
|
||||
**
|
||||
** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
|
||||
** This option is used to set, clear or query the flag that enables
|
||||
** the [sqlite3session_changeset_size()] API. Because it imposes some
|
||||
** computational overhead, this API is disabled by default. Argument
|
||||
** pArg must point to a value of type (int). If the value is initially
|
||||
** 0, then the sqlite3session_changeset_size() API is disabled. If it
|
||||
** is greater than 0, then the same API is enabled. Or, if the initial
|
||||
** value is less than zero, no change is made. In all cases the (int)
|
||||
** variable is set to 1 if the sqlite3session_changeset_size() API is
|
||||
** enabled following the current call, or 0 otherwise.
|
||||
**
|
||||
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
|
||||
** the first table has been attached to the session object.
|
||||
*/
|
||||
SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
|
||||
|
||||
/*
|
||||
*/
|
||||
#define SQLITE_SESSION_OBJCONFIG_SIZE 1
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable A Session Object
|
||||
|
@ -10335,6 +10891,22 @@ SQLITE_API int sqlite3session_changeset(
|
|||
void **ppChangeset /* OUT: Buffer containing changeset */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
|
||||
** METHOD: sqlite3_session
|
||||
**
|
||||
** By default, this function always returns 0. For it to return
|
||||
** a useful result, the sqlite3_session object must have been configured
|
||||
** to enable this API using sqlite3session_object_config() with the
|
||||
** SQLITE_SESSION_OBJCONFIG_SIZE verb.
|
||||
**
|
||||
** When enabled, this function returns an upper limit, in bytes, for the size
|
||||
** of the changeset that might be produced if sqlite3session_changeset() were
|
||||
** called. The final changeset size might be equal to or smaller than the
|
||||
** size in bytes returned by this function.
|
||||
*/
|
||||
SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Load The Difference Between Tables Into A Session
|
||||
** METHOD: sqlite3_session
|
||||
|
|
|
@ -22,7 +22,7 @@ Bool *Bool::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in Bool", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in Bool", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -40,7 +40,7 @@ void TL_boolFalse::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_dcOption *TL_dcOption::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_dcOption::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_dcOption", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_dcOption", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_dcOption *result = new TL_dcOption();
|
||||
|
@ -55,6 +55,8 @@ void TL_dcOption::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool
|
|||
tcpo_only = (flags & 4) != 0;
|
||||
cdn = (flags & 8) != 0;
|
||||
isStatic = (flags & 16) != 0;
|
||||
thisPortOnly = (flags & 32) != 0;
|
||||
force_try_ipv6 = (flags & 16384) != 0;
|
||||
id = stream->readInt32(&error);
|
||||
ip_address = stream->readString(&error);
|
||||
port = stream->readInt32(&error);
|
||||
|
@ -70,6 +72,8 @@ void TL_dcOption::serializeToStream(NativeByteBuffer *stream) {
|
|||
flags = tcpo_only ? (flags | 4) : (flags &~ 4);
|
||||
flags = cdn ? (flags | 8) : (flags &~ 8);
|
||||
flags = isStatic ? (flags | 16) : (flags &~ 16);
|
||||
flags = thisPortOnly ? (flags | 32) : (flags &~ 32);
|
||||
flags = force_try_ipv6 ? (flags | 16384) : (flags &~ 16384);
|
||||
stream->writeInt32(flags);
|
||||
stream->writeInt32(id);
|
||||
stream->writeString(ip_address);
|
||||
|
@ -82,7 +86,7 @@ void TL_dcOption::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_cdnPublicKey *TL_cdnPublicKey::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_cdnPublicKey::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_cdnPublicKey", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_cdnPublicKey", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_cdnPublicKey *result = new TL_cdnPublicKey();
|
||||
|
@ -104,7 +108,7 @@ void TL_cdnPublicKey::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_cdnConfig *TL_cdnConfig::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_cdnConfig::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_cdnConfig", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_cdnConfig", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_cdnConfig *result = new TL_cdnConfig();
|
||||
|
@ -116,7 +120,7 @@ void TL_cdnConfig::readParams(NativeByteBuffer *stream, int32_t instanceNum, boo
|
|||
int magic = stream->readInt32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_cdnConfig, got %x", magic);
|
||||
return;
|
||||
}
|
||||
int count = stream->readInt32(&error);
|
||||
|
@ -154,7 +158,7 @@ void TL_help_getCdnConfig::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_config *TL_config::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_config::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_config", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_config", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_config *result = new TL_config();
|
||||
|
@ -171,7 +175,7 @@ void TL_config::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &
|
|||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_config, got %x", magic);
|
||||
return;
|
||||
}
|
||||
int32_t count = stream->readInt32(&error);
|
||||
|
@ -194,19 +198,19 @@ void TL_config::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &
|
|||
notify_default_delay_ms = stream->readInt32(&error);
|
||||
push_chat_period_ms = stream->readInt32(&error);
|
||||
push_chat_limit = stream->readInt32(&error);
|
||||
saved_gifs_limit = stream->readInt32(&error);
|
||||
// saved_gifs_limit = stream->readInt32(&error);
|
||||
edit_time_limit = stream->readInt32(&error);
|
||||
revoke_time_limit = stream->readInt32(&error);
|
||||
revoke_pm_time_limit = stream->readInt32(&error);
|
||||
rating_e_decay = stream->readInt32(&error);
|
||||
stickers_recent_limit = stream->readInt32(&error);
|
||||
stickers_faved_limit = stream->readInt32(&error);
|
||||
// stickers_faved_limit = stream->readInt32(&error);
|
||||
channels_read_media_period = stream->readInt32(&error);
|
||||
if ((flags & 1) != 0) {
|
||||
tmp_sessions = stream->readInt32(&error);
|
||||
}
|
||||
pinned_dialogs_count_max = stream->readInt32(&error);
|
||||
pinned_infolder_count_max = stream->readInt32(&error);
|
||||
// pinned_dialogs_count_max = stream->readInt32(&error);
|
||||
// pinned_infolder_count_max = stream->readInt32(&error);
|
||||
call_receive_timeout_ms = stream->readInt32(&error);
|
||||
call_ring_timeout_ms = stream->readInt32(&error);
|
||||
call_connect_timeout_ms = stream->readInt32(&error);
|
||||
|
@ -239,6 +243,12 @@ void TL_config::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &
|
|||
if ((flags & 4) != 0) {
|
||||
base_lang_pack_version = stream->readInt32(&error);
|
||||
}
|
||||
if ((flags & 32768) != 0) {
|
||||
reactions_default = std::unique_ptr<Reaction>(Reaction::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
||||
}
|
||||
if ((flags & 65536) != 0) {
|
||||
autologin_token = stream->readString(&error);
|
||||
}
|
||||
}
|
||||
|
||||
void TL_config::serializeToStream(NativeByteBuffer *stream) {
|
||||
|
@ -266,19 +276,19 @@ void TL_config::serializeToStream(NativeByteBuffer *stream) {
|
|||
stream->writeInt32(notify_default_delay_ms);
|
||||
stream->writeInt32(push_chat_period_ms);
|
||||
stream->writeInt32(push_chat_limit);
|
||||
stream->writeInt32(saved_gifs_limit);
|
||||
// stream->writeInt32(saved_gifs_limit);
|
||||
stream->writeInt32(edit_time_limit);
|
||||
stream->writeInt32(revoke_time_limit);
|
||||
stream->writeInt32(revoke_pm_time_limit);
|
||||
stream->writeInt32(rating_e_decay);
|
||||
stream->writeInt32(stickers_recent_limit);
|
||||
stream->writeInt32(stickers_faved_limit);
|
||||
// stream->writeInt32(stickers_faved_limit);
|
||||
stream->writeInt32(channels_read_media_period);
|
||||
if ((flags & 1) != 0) {
|
||||
stream->writeInt32(tmp_sessions);
|
||||
}
|
||||
stream->writeInt32(pinned_dialogs_count_max);
|
||||
stream->writeInt32(pinned_infolder_count_max);
|
||||
// stream->writeInt32(pinned_dialogs_count_max);
|
||||
// stream->writeInt32(pinned_infolder_count_max);
|
||||
stream->writeInt32(call_receive_timeout_ms);
|
||||
stream->writeInt32(call_ring_timeout_ms);
|
||||
stream->writeInt32(call_connect_timeout_ms);
|
||||
|
@ -311,6 +321,12 @@ void TL_config::serializeToStream(NativeByteBuffer *stream) {
|
|||
if ((flags & 4) != 0) {
|
||||
stream->writeInt32(base_lang_pack_version);
|
||||
}
|
||||
if ((flags & 32768) != 0 && reactions_default != nullptr) {
|
||||
reactions_default->serializeToStream(stream);
|
||||
}
|
||||
if ((flags & 65536) != 0) {
|
||||
stream->writeString(autologin_token);
|
||||
}
|
||||
}
|
||||
|
||||
TLObject *TL_help_getConfig::deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
|
@ -342,7 +358,7 @@ void TL_account_registerDevice::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_restrictionReason *TL_restrictionReason::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_restrictionReason::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_restrictionReason", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_restrictionReason", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_restrictionReason *result = new TL_restrictionReason();
|
||||
|
@ -350,6 +366,17 @@ TL_restrictionReason *TL_restrictionReason::TLdeserialize(NativeByteBuffer *stre
|
|||
return result;
|
||||
}
|
||||
|
||||
TL_username *TL_username::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_username::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_username", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_username *result = new TL_username();
|
||||
result->readParams(stream, instanceNum, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
void TL_restrictionReason::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
platform = stream->readString(&error);
|
||||
reason = stream->readString(&error);
|
||||
|
@ -363,18 +390,33 @@ void TL_restrictionReason::serializeToStream(NativeByteBuffer *stream) {
|
|||
stream->writeString(text);
|
||||
}
|
||||
|
||||
void TL_username::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
flags = stream->readInt32(&error);
|
||||
editable = (flags & 1) != 0;
|
||||
active = (flags & 2) != 0;
|
||||
username = stream->readString(&error);
|
||||
}
|
||||
|
||||
void TL_username::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
flags = editable ? (flags | 1) : (flags &~ 1);
|
||||
flags = active ? (flags | 2) : (flags &~ 2);
|
||||
stream->writeInt32(flags);
|
||||
stream->writeString(username);
|
||||
}
|
||||
|
||||
User *User::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
User *result = nullptr;
|
||||
switch (constructor) {
|
||||
case 0x200250ba:
|
||||
case TL_userEmpty::constructor:
|
||||
result = new TL_userEmpty();
|
||||
break;
|
||||
case 0x938458c1:
|
||||
case TL_user::constructor:
|
||||
result = new TL_user();
|
||||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in User", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in User", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -382,17 +424,18 @@ User *User::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_
|
|||
}
|
||||
|
||||
void TL_userEmpty::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
id = stream->readInt32(&error);
|
||||
id = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_userEmpty::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(id);
|
||||
stream->writeInt64(id);
|
||||
}
|
||||
|
||||
void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
flags = stream->readInt32(&error);
|
||||
id = stream->readInt32(&error);
|
||||
flags2 = stream->readInt32(&error);
|
||||
id = stream->readInt64(&error);
|
||||
if ((flags & 1) != 0) {
|
||||
access_hash = stream->readInt64(&error);
|
||||
}
|
||||
|
@ -421,7 +464,7 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
|
|||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_user, got %x", magic);
|
||||
return;
|
||||
}
|
||||
int32_t count = stream->readInt32(&error);
|
||||
|
@ -439,12 +482,45 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
|
|||
if ((flags & 4194304) != 0) {
|
||||
lang_code = stream->readString(&error);
|
||||
}
|
||||
if ((flags & 1073741824) != 0) {
|
||||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic == 0x2de11aae) {
|
||||
// emojiStatusEmpty
|
||||
} else if (magic == 0x929b619d) {
|
||||
// emojiStatus
|
||||
int64_t document_id = stream->readInt64(&error);
|
||||
} else if (magic == 0xfa30a8c7) {
|
||||
// emojiStatusUntil
|
||||
int64_t document_id = stream->readInt64(&error);
|
||||
int until = stream->readInt32(&error);
|
||||
} else {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong EmojiStatus magic, got %x", magic);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((flags2 & 1) != 0) {
|
||||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_user (2), got %x", magic);
|
||||
return;
|
||||
}
|
||||
int32_t count = stream->readInt32(&error);
|
||||
for (int32_t a = 0; a < count; a++) {
|
||||
TL_username *object = TL_username::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error);
|
||||
if (object == nullptr) {
|
||||
return;
|
||||
}
|
||||
usernames.push_back(std::unique_ptr<TL_username>(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TL_user::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(flags);
|
||||
stream->writeInt32(id);
|
||||
stream->writeInt64(id);
|
||||
if ((flags & 1) != 0) {
|
||||
stream->writeInt64(access_hash);
|
||||
}
|
||||
|
@ -491,19 +567,19 @@ InputPeer *InputPeer::TLdeserialize(NativeByteBuffer *stream, uint32_t construct
|
|||
case 0x7da07ec9:
|
||||
result = new TL_inputPeerSelf();
|
||||
break;
|
||||
case 0x7b8e7de6:
|
||||
case 0xdde8a54c:
|
||||
result = new TL_inputPeerUser();
|
||||
break;
|
||||
case 0x179be863:
|
||||
case 0x35a95cb9:
|
||||
result = new TL_inputPeerChat();
|
||||
break;
|
||||
case 0x17bae2e6:
|
||||
case 0xa87b0a1c:
|
||||
result = new TL_inputPeerUserFromMessage();
|
||||
break;
|
||||
case 0x9c95f7bb:
|
||||
case 0xbd2a0840:
|
||||
result = new TL_inputPeerChannelFromMessage();
|
||||
break;
|
||||
case 0x20adaef8:
|
||||
case 0x27bcbbfc:
|
||||
result = new TL_inputPeerChannel();
|
||||
break;
|
||||
case 0x7f3b18ea:
|
||||
|
@ -511,7 +587,7 @@ InputPeer *InputPeer::TLdeserialize(NativeByteBuffer *stream, uint32_t construct
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in InputPeer", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in InputPeer", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -523,59 +599,59 @@ void TL_inputPeerSelf::serializeToStream(NativeByteBuffer *stream) {
|
|||
}
|
||||
|
||||
void TL_inputPeerUser::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
user_id = stream->readInt32(&error);
|
||||
user_id = stream->readInt64(&error);
|
||||
access_hash = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_inputPeerUser::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(user_id);
|
||||
stream->writeInt64(user_id);
|
||||
stream->writeInt64(access_hash);
|
||||
}
|
||||
|
||||
void TL_inputPeerChat::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
chat_id = stream->readInt32(&error);
|
||||
chat_id = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_inputPeerChat::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(chat_id);
|
||||
stream->writeInt64(chat_id);
|
||||
}
|
||||
|
||||
void TL_inputPeerUserFromMessage::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
peer = std::unique_ptr<InputPeer>(InputPeer::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
||||
msg_id = stream->readInt32(&error);
|
||||
user_id = stream->readInt32(&error);
|
||||
user_id = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_inputPeerUserFromMessage::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
peer->serializeToStream(stream);
|
||||
stream->writeInt32(msg_id);
|
||||
stream->writeInt32(user_id);
|
||||
stream->writeInt64(user_id);
|
||||
}
|
||||
|
||||
void TL_inputPeerChannelFromMessage::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
peer = std::unique_ptr<InputPeer>(InputPeer::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
||||
msg_id = stream->readInt32(&error);
|
||||
channel_id = stream->readInt32(&error);
|
||||
channel_id = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_inputPeerChannelFromMessage::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
peer->serializeToStream(stream);
|
||||
stream->writeInt32(msg_id);
|
||||
stream->writeInt32(channel_id);
|
||||
stream->writeInt64(channel_id);
|
||||
}
|
||||
|
||||
void TL_inputPeerChannel::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
channel_id = stream->readInt32(&error);
|
||||
channel_id = stream->readInt64(&error);
|
||||
access_hash = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_inputPeerChannel::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(channel_id);
|
||||
stream->writeInt64(channel_id);
|
||||
stream->writeInt64(access_hash);
|
||||
}
|
||||
|
||||
|
@ -589,18 +665,18 @@ InputUser *InputUser::TLdeserialize(NativeByteBuffer *stream, uint32_t construct
|
|||
case 0xf7c1b13f:
|
||||
result = new TL_inputUserSelf();
|
||||
break;
|
||||
case 0xd8292816:
|
||||
case 0xf21158c6:
|
||||
result = new TL_inputUser();
|
||||
break;
|
||||
case 0xb98886cf:
|
||||
result = new TL_inputUserEmpty();
|
||||
break;
|
||||
case 0x2d117597:
|
||||
case 0x1da448e2:
|
||||
result = new TL_inputUserFromMessage();
|
||||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in InputUser", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in InputUser", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -612,13 +688,13 @@ void TL_inputUserSelf::serializeToStream(NativeByteBuffer *stream) {
|
|||
}
|
||||
|
||||
void TL_inputUser::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
user_id = stream->readInt32(&error);
|
||||
user_id = stream->readInt64(&error);
|
||||
access_hash = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_inputUser::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(user_id);
|
||||
stream->writeInt64(user_id);
|
||||
stream->writeInt64(access_hash);
|
||||
}
|
||||
|
||||
|
@ -629,14 +705,14 @@ void TL_inputUserEmpty::serializeToStream(NativeByteBuffer *stream) {
|
|||
void TL_inputUserFromMessage::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
peer = std::unique_ptr<InputPeer>(InputPeer::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
||||
msg_id = stream->readInt32(&error);
|
||||
user_id = stream->readInt32(&error);
|
||||
user_id = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_inputUserFromMessage::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
peer->serializeToStream(stream);
|
||||
stream->writeInt32(msg_id);
|
||||
stream->writeInt32(user_id);
|
||||
stream->writeInt64(user_id);
|
||||
}
|
||||
|
||||
MessageEntity *MessageEntity::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
|
@ -666,7 +742,7 @@ MessageEntity *MessageEntity::TLdeserialize(NativeByteBuffer *stream, uint32_t c
|
|||
case 0xfa04579d:
|
||||
result = new TL_messageEntityMention();
|
||||
break;
|
||||
case 0x352dca58:
|
||||
case 0xdc7b1140:
|
||||
result = new TL_messageEntityMentionName();
|
||||
break;
|
||||
case 0x208e68c9:
|
||||
|
@ -698,7 +774,7 @@ MessageEntity *MessageEntity::TLdeserialize(NativeByteBuffer *stream, uint32_t c
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in MessageEntity", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in MessageEntity", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -800,14 +876,14 @@ void TL_messageEntityMention::serializeToStream(NativeByteBuffer *stream) {
|
|||
void TL_messageEntityMentionName::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
offset = stream->readInt32(&error);
|
||||
length = stream->readInt32(&error);
|
||||
user_id = stream->readInt32(&error);
|
||||
user_id = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_messageEntityMentionName::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(offset);
|
||||
stream->writeInt32(length);
|
||||
stream->writeInt32(user_id);
|
||||
stream->writeInt64(user_id);
|
||||
}
|
||||
|
||||
void TL_inputMessageEntityMentionName::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
|
@ -914,7 +990,7 @@ void TL_messageEntityPhone::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_dataJSON *TL_dataJSON::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_dataJSON::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_dataJSON", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_dataJSON", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_dataJSON *result = new TL_dataJSON();
|
||||
|
@ -934,7 +1010,7 @@ void TL_dataJSON::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_help_termsOfService *TL_help_termsOfService::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_help_termsOfService::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_help_termsOfService", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_help_termsOfService", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_help_termsOfService *result = new TL_help_termsOfService();
|
||||
|
@ -950,7 +1026,7 @@ void TL_help_termsOfService::readParams(NativeByteBuffer *stream, int32_t instan
|
|||
int magic = stream->readInt32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_help_termsOfService, got %x", magic);
|
||||
return;
|
||||
}
|
||||
int count = stream->readInt32(&error);
|
||||
|
@ -989,12 +1065,12 @@ auth_Authorization *auth_Authorization::TLdeserialize(NativeByteBuffer *stream,
|
|||
case 0x44747e9a:
|
||||
result = new TL_auth_authorizationSignUpRequired();
|
||||
break;
|
||||
case 0xcd050916:
|
||||
case 0x2ea2c0d4:
|
||||
result = new TL_auth_authorization();
|
||||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in auth_Authorization", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in auth_Authorization", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -1018,9 +1094,15 @@ void TL_auth_authorizationSignUpRequired::serializeToStream(NativeByteBuffer *st
|
|||
|
||||
void TL_auth_authorization::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
flags = stream->readInt32(&error);
|
||||
if ((flags & 2) != 0) {
|
||||
otherwise_relogin_days = stream->readInt32(&error);
|
||||
}
|
||||
if ((flags & 1) != 0) {
|
||||
tmp_sessions = stream->readInt32(&error);
|
||||
}
|
||||
if ((flags & 4) != 0) {
|
||||
future_auth_token = std::unique_ptr<ByteArray>(stream->readByteArray(&error));
|
||||
}
|
||||
user = std::unique_ptr<User>(User::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
|
||||
}
|
||||
|
||||
|
@ -1036,7 +1118,7 @@ void TL_auth_authorization::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_auth_exportedAuthorization *TL_auth_exportedAuthorization::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_auth_exportedAuthorization::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_auth_exportedAuthorization", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_auth_exportedAuthorization", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_auth_exportedAuthorization *result = new TL_auth_exportedAuthorization();
|
||||
|
@ -1045,7 +1127,7 @@ TL_auth_exportedAuthorization *TL_auth_exportedAuthorization::TLdeserialize(Nati
|
|||
}
|
||||
|
||||
void TL_auth_exportedAuthorization::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
id = stream->readInt32(&error);
|
||||
id = stream->readInt64(&error);
|
||||
bytes = std::unique_ptr<ByteArray>(stream->readByteArray(&error));
|
||||
}
|
||||
|
||||
|
@ -1072,7 +1154,7 @@ TLObject *TL_auth_importAuthorization::deserializeResponse(NativeByteBuffer *str
|
|||
|
||||
void TL_auth_importAuthorization::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt32(id);
|
||||
stream->writeInt64(id);
|
||||
stream->writeByteArray(bytes.get());
|
||||
}
|
||||
|
||||
|
@ -1099,7 +1181,7 @@ UserStatus *UserStatus::TLdeserialize(NativeByteBuffer *stream, uint32_t constru
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in UserStatus", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in UserStatus", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -1148,7 +1230,7 @@ FileLocation *FileLocation::TLdeserialize(NativeByteBuffer *stream, uint32_t con
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in FileLocation", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in FileLocation", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -1177,7 +1259,7 @@ UserProfilePhoto *UserProfilePhoto::TLdeserialize(NativeByteBuffer *stream, uint
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in UserProfilePhoto", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in UserProfilePhoto", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -1212,3 +1294,52 @@ void TL_userProfilePhoto::serializeToStream(NativeByteBuffer *stream) {
|
|||
void TL_updatesTooLong::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
}
|
||||
|
||||
Reaction *Reaction::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
Reaction *result = nullptr;
|
||||
switch (constructor) {
|
||||
case 0x79f5d419:
|
||||
result = new TL_reactionEmpty();
|
||||
break;
|
||||
case 0x8935fc73:
|
||||
result = new TL_reactionCustomEmoji();
|
||||
break;
|
||||
case 0x1b2286b8:
|
||||
result = new TL_reactionEmoji();
|
||||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in Reaction", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
void TL_reactionEmpty::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
|
||||
}
|
||||
|
||||
void TL_reactionEmpty::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
}
|
||||
|
||||
void TL_reactionCustomEmoji::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
document_id = stream->readInt64(&error);
|
||||
}
|
||||
|
||||
void TL_reactionCustomEmoji::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeInt64(document_id);
|
||||
}
|
||||
|
||||
void TL_reactionEmoji::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
|
||||
emoticon = stream->readString(&error);
|
||||
}
|
||||
|
||||
void TL_reactionEmoji::serializeToStream(NativeByteBuffer *stream) {
|
||||
stream->writeInt32(constructor);
|
||||
stream->writeString(emoticon);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ public:
|
|||
bool tcpo_only;
|
||||
bool cdn;
|
||||
bool isStatic;
|
||||
bool thisPortOnly;
|
||||
bool force_try_ipv6;
|
||||
int32_t id;
|
||||
std::string ip_address;
|
||||
int32_t port;
|
||||
|
@ -94,10 +96,17 @@ public:
|
|||
void serializeToStream(NativeByteBuffer *stream);
|
||||
};
|
||||
|
||||
class Reaction : public TLObject {
|
||||
|
||||
public:
|
||||
static Reaction *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
};
|
||||
|
||||
|
||||
class TL_config : public TLObject {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x330b4067;
|
||||
static const uint32_t constructor = 0xcc1a241e;
|
||||
|
||||
int32_t flags;
|
||||
int32_t date;
|
||||
|
@ -117,17 +126,17 @@ public:
|
|||
int32_t notify_default_delay_ms;
|
||||
int32_t push_chat_period_ms;
|
||||
int32_t push_chat_limit;
|
||||
int32_t saved_gifs_limit;
|
||||
// int32_t saved_gifs_limit;
|
||||
int32_t edit_time_limit;
|
||||
int32_t revoke_time_limit;
|
||||
int32_t revoke_pm_time_limit;
|
||||
int32_t rating_e_decay;
|
||||
int32_t stickers_recent_limit;
|
||||
int32_t stickers_faved_limit;
|
||||
// int32_t stickers_faved_limit;
|
||||
int32_t channels_read_media_period;
|
||||
int32_t tmp_sessions;
|
||||
int32_t pinned_dialogs_count_max;
|
||||
int32_t pinned_infolder_count_max;
|
||||
// int32_t pinned_dialogs_count_max;
|
||||
// int32_t pinned_infolder_count_max;
|
||||
int32_t call_receive_timeout_ms;
|
||||
int32_t call_ring_timeout_ms;
|
||||
int32_t call_connect_timeout_ms;
|
||||
|
@ -144,6 +153,8 @@ public:
|
|||
std::string suggested_lang_code;
|
||||
int32_t lang_pack_version;
|
||||
int32_t base_lang_pack_version;
|
||||
std::unique_ptr<Reaction> reactions_default;
|
||||
std::string autologin_token;
|
||||
|
||||
static TL_config *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
|
@ -292,10 +303,24 @@ public:
|
|||
void serializeToStream(NativeByteBuffer *stream);
|
||||
};
|
||||
|
||||
class TL_username : public TLObject {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xb4073647;
|
||||
int32_t flags;
|
||||
bool editable;
|
||||
bool active;
|
||||
std::string username;
|
||||
|
||||
static TL_username *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
};
|
||||
|
||||
class User : public TLObject {
|
||||
|
||||
public:
|
||||
int32_t id;
|
||||
int64_t id;
|
||||
std::string first_name;
|
||||
std::string last_name;
|
||||
std::string username;
|
||||
|
@ -304,10 +329,12 @@ public:
|
|||
std::unique_ptr<UserProfilePhoto> photo;
|
||||
std::unique_ptr<UserStatus> status;
|
||||
int32_t flags;
|
||||
int32_t flags2;
|
||||
int32_t bot_info_version;
|
||||
std::vector<std::unique_ptr<TL_restrictionReason>> restriction_reason;
|
||||
std::string bot_inline_placeholder;
|
||||
std::string lang_code;
|
||||
std::vector<std::unique_ptr<TL_username>> usernames;
|
||||
|
||||
static User *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
};
|
||||
|
@ -315,7 +342,7 @@ public:
|
|||
class TL_userEmpty : public User {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x200250ba;
|
||||
static const uint32_t constructor = 0xd3bc4b7a;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -324,7 +351,7 @@ public:
|
|||
class TL_user : public User {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x938458c1;
|
||||
static const uint32_t constructor = 0x8f97c628;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -333,9 +360,9 @@ public:
|
|||
class InputPeer : public TLObject {
|
||||
|
||||
public:
|
||||
int32_t user_id;
|
||||
int32_t chat_id;
|
||||
int32_t channel_id;
|
||||
int64_t user_id;
|
||||
int64_t chat_id;
|
||||
int64_t channel_id;
|
||||
int64_t access_hash;
|
||||
|
||||
static InputPeer *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
|
@ -352,7 +379,7 @@ public:
|
|||
class TL_inputPeerUser : public InputPeer {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x7b8e7de6;
|
||||
static const uint32_t constructor = 0xdde8a54c;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -361,7 +388,7 @@ public:
|
|||
class TL_inputPeerChat : public InputPeer {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x179be863;
|
||||
static const uint32_t constructor = 0x35a95cb9;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -370,7 +397,7 @@ public:
|
|||
class TL_inputPeerUserFromMessage : public InputPeer {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x17bae2e6;
|
||||
static const uint32_t constructor = 0xa87b0a1c;
|
||||
|
||||
std::unique_ptr<InputPeer> peer;
|
||||
int32_t msg_id;
|
||||
|
@ -382,7 +409,7 @@ public:
|
|||
class TL_inputPeerChannelFromMessage : public InputPeer {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x9c95f7bb;
|
||||
static const uint32_t constructor = 0xbd2a0840;
|
||||
|
||||
std::unique_ptr<InputPeer> peer;
|
||||
int32_t msg_id;
|
||||
|
@ -394,7 +421,7 @@ public:
|
|||
class TL_inputPeerChannel : public InputPeer {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x20adaef8;
|
||||
static const uint32_t constructor = 0x27bcbbfc;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -411,7 +438,7 @@ public:
|
|||
class InputUser : public TLObject {
|
||||
|
||||
public:
|
||||
int32_t user_id;
|
||||
int64_t user_id;
|
||||
int64_t access_hash;
|
||||
|
||||
static InputUser *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
|
@ -428,7 +455,7 @@ public:
|
|||
class TL_inputUser : public InputUser {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xd8292816;
|
||||
static const uint32_t constructor = 0xf21158c6;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -445,7 +472,7 @@ public:
|
|||
class TL_inputUserFromMessage : public InputUser {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x2d117597;
|
||||
static const uint32_t constructor = 0x1da448e2;
|
||||
|
||||
std::unique_ptr<InputPeer> peer;
|
||||
int32_t msg_id;
|
||||
|
@ -539,9 +566,9 @@ public:
|
|||
class TL_messageEntityMentionName : public MessageEntity {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x352dca58;
|
||||
static const uint32_t constructor = 0xdc7b1140;
|
||||
|
||||
int32_t user_id;
|
||||
int64_t user_id;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
@ -680,10 +707,12 @@ public:
|
|||
class TL_auth_authorization : public auth_Authorization {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xcd050916;
|
||||
static const uint32_t constructor = 0x2ea2c0d4;
|
||||
|
||||
int32_t flags;
|
||||
int32_t tmp_sessions;
|
||||
int32_t otherwise_relogin_days;
|
||||
std::unique_ptr<ByteArray> future_auth_token;
|
||||
std::unique_ptr<User> user;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
|
@ -693,9 +722,9 @@ public:
|
|||
class TL_auth_exportedAuthorization : public TLObject {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xdf969c2d;
|
||||
static const uint32_t constructor = 0xb434e2b8;
|
||||
|
||||
int32_t id;
|
||||
int64_t id;
|
||||
std::unique_ptr<ByteArray> bytes;
|
||||
|
||||
static TL_auth_exportedAuthorization *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
|
||||
|
@ -717,9 +746,9 @@ public:
|
|||
class TL_auth_importAuthorization : public TLObject {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xe3ef9613;
|
||||
static const uint32_t constructor = 0xa57a7dad;
|
||||
|
||||
int32_t id;
|
||||
int64_t id;
|
||||
std::unique_ptr<ByteArray> bytes;
|
||||
|
||||
bool isNeedLayer();
|
||||
|
@ -735,4 +764,37 @@ public:
|
|||
void serializeToStream(NativeByteBuffer *stream);
|
||||
};
|
||||
|
||||
|
||||
class TL_reactionCustomEmoji : public Reaction {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x8935fc73;
|
||||
int64_t document_id;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
};
|
||||
|
||||
|
||||
class TL_reactionEmoji : public Reaction {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x1b2286b8;
|
||||
std::string emoticon;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TL_reactionEmpty : public Reaction {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0x79f5d419;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -699,6 +699,10 @@ void Connection::onDisconnectedInternal(int32_t reason, int32_t error) {
|
|||
isTryingNextPort = true;
|
||||
if (failedConnectionCount > willRetryConnectCount || switchToNextPort) {
|
||||
currentDatacenter->nextAddressOrPort(currentAddressFlags);
|
||||
if (currentDatacenter->isRepeatCheckingAddresses() && (ConnectionsManager::getInstance(currentDatacenter->instanceNum).getIpStratagy() == USE_IPV4_ONLY || ConnectionsManager::getInstance(currentDatacenter->instanceNum).getIpStratagy() == USE_IPV6_ONLY)) {
|
||||
if (LOGS_ENABLED) DEBUG_D("started retrying connection, set ipv4 ipv6 random strategy");
|
||||
ConnectionsManager::getInstance(currentDatacenter->instanceNum).setIpStrategy(USE_IPV4_IPV6_RANDOM);
|
||||
}
|
||||
failedConnectionCount = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,12 +35,6 @@ public:
|
|||
void setOverrideProxy(std::string address, uint16_t port, std::string username, std::string password, std::string secret);
|
||||
void onHostNameResolved(std::string host, std::string ip, bool ipv6);
|
||||
|
||||
std::string overrideProxyUser = "";
|
||||
std::string overrideProxyPassword = "";
|
||||
std::string overrideProxyAddress = "";
|
||||
std::string overrideProxySecret = "";
|
||||
uint16_t overrideProxyPort = 1080;
|
||||
|
||||
protected:
|
||||
int32_t instanceNum;
|
||||
void onEvent(uint32_t events);
|
||||
|
@ -52,6 +46,12 @@ protected:
|
|||
virtual void onConnected() = 0;
|
||||
virtual bool hasPendingRequests() = 0;
|
||||
|
||||
std::string overrideProxyUser = "";
|
||||
std::string overrideProxyPassword = "";
|
||||
std::string overrideProxyAddress = "";
|
||||
std::string overrideProxySecret = "";
|
||||
uint16_t overrideProxyPort = 1080;
|
||||
|
||||
private:
|
||||
ByteStream *outgoingByteStream = nullptr;
|
||||
struct epoll_event eventMask;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "ByteArray.h"
|
||||
#include "Config.h"
|
||||
#include "ProxyCheckInfo.h"
|
||||
#include "Handshake.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <jni.h>
|
||||
|
@ -618,7 +619,7 @@ void ConnectionsManager::cleanUp(bool resetKeys, int32_t datacenterId) {
|
|||
auto error = new TL_error();
|
||||
error->code = -1000;
|
||||
error->text = "";
|
||||
request->onComplete(nullptr, error, 0, 0);
|
||||
request->onComplete(nullptr, error, 0, 0, request->messageId);
|
||||
delete error;
|
||||
}
|
||||
iter = requestsQueue.erase(iter);
|
||||
|
@ -640,7 +641,7 @@ void ConnectionsManager::cleanUp(bool resetKeys, int32_t datacenterId) {
|
|||
auto error = new TL_error();
|
||||
error->code = -1000;
|
||||
error->text = "";
|
||||
request->onComplete(nullptr, error, 0, 0);
|
||||
request->onComplete(nullptr, error, 0, 0, request->messageId);
|
||||
delete error;
|
||||
}
|
||||
iter = runningRequests.erase(iter);
|
||||
|
@ -888,10 +889,12 @@ void ConnectionsManager::onConnectionDataReceived(Connection *connection, Native
|
|||
|
||||
if (object != nullptr) {
|
||||
if (datacenter->isHandshaking(connection->isMediaConnection)) {
|
||||
if (LOGS_ENABLED) DEBUG_E("process handshake");
|
||||
datacenter->processHandshakeResponse(connection->isMediaConnection, object, messageId);
|
||||
} else {
|
||||
processServerResponse(object, messageId, 0, 0, connection, 0, 0);
|
||||
connection->addProcessedMessageId(messageId);
|
||||
if (LOGS_ENABLED) DEBUG_E("connection(%p) received incorrect unencrypted message type", connection);
|
||||
connection->reconnect();
|
||||
return;
|
||||
}
|
||||
lastProtocolUsefullData = true;
|
||||
connection->setHasUsefullData();
|
||||
|
@ -1177,7 +1180,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
|
|||
for (auto iter = runningRequests.begin(); iter != runningRequests.end(); iter++) {
|
||||
Request *request = iter->get();
|
||||
if (request->respondsToMessageId(requestMid)) {
|
||||
request->onComplete(response, nullptr, connection->currentNetworkType, timeMessage);
|
||||
request->onComplete(response, nullptr, connection->currentNetworkType, timeMessage, requestMid);
|
||||
request->completed = true;
|
||||
runningRequests.erase(iter);
|
||||
break;
|
||||
|
@ -1197,11 +1200,11 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
|
|||
bool ignoreResult = false;
|
||||
if (hasResult) {
|
||||
TLObject *object = response->result.get();
|
||||
if (LOGS_ENABLED) DEBUG_D("connection(%p, account%u, dc%u, type %d) received rpc_result with %s", connection, instanceNum, datacenter->getDatacenterId(), connection->getConnectionType(), typeid(*object).name());
|
||||
if (LOGS_ENABLED) DEBUG_D("message_id %lld connection(%p, account%u, dc%u, type %d) received rpc_result with %s", messageId, connection, instanceNum, datacenter->getDatacenterId(), connection->getConnectionType(), typeid(*object).name());
|
||||
}
|
||||
RpcError *error = hasResult ? dynamic_cast<RpcError *>(response->result.get()) : nullptr;
|
||||
if (error != nullptr) {
|
||||
if (LOGS_ENABLED) DEBUG_E("connection(%p, account%u, dc%u, type %d) rpc error %d: %s", connection, instanceNum, datacenter->getDatacenterId(), connection->getConnectionType(), error->error_code, error->error_message.c_str());
|
||||
if (LOGS_ENABLED) DEBUG_E("message_id %lld connection(%p, account%u, dc%u, type %d) rpc error %d: %s", messageId, connection, instanceNum, datacenter->getDatacenterId(), connection->getConnectionType(), error->error_code, error->error_message.c_str());
|
||||
if (error->error_code == 303) {
|
||||
uint32_t migrateToDatacenterId = DEFAULT_DATACENTER_ID;
|
||||
|
||||
|
@ -1296,7 +1299,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
|
|||
int32_t waitTime = 2;
|
||||
static std::string floodWait = "FLOOD_WAIT_";
|
||||
static std::string slowmodeWait = "SLOWMODE_WAIT_";
|
||||
discardResponse = true;
|
||||
discardResponse = (request->requestFlags & RequestFlagIgnoreFloodWait) == 0;
|
||||
if (error->error_message.find(floodWait) != std::string::npos) {
|
||||
std::string num = error->error_message.substr(floodWait.size(), error->error_message.size() - floodWait.size());
|
||||
waitTime = atoi(num.c_str());
|
||||
|
@ -1354,10 +1357,10 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
|
|||
if (!discardResponse) {
|
||||
if (implicitError != nullptr || error2 != nullptr) {
|
||||
isError = true;
|
||||
request->onComplete(nullptr, implicitError != nullptr ? implicitError : error2, connection->currentNetworkType, timeMessage);
|
||||
request->onComplete(nullptr, implicitError != nullptr ? implicitError : error2, connection->currentNetworkType, timeMessage, request->messageId);
|
||||
delete error2;
|
||||
} else {
|
||||
request->onComplete(response->result.get(), nullptr, connection->currentNetworkType, timeMessage);
|
||||
request->onComplete(response->result.get(), nullptr, connection->currentNetworkType, timeMessage, request->messageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1763,6 +1766,13 @@ int32_t ConnectionsManager::sendRequestInternal(TLObject *object, onCompleteFunc
|
|||
auto request = new Request(instanceNum, lastRequestToken++, connetionType, flags, datacenterId, onComplete, onQuickAck, nullptr);
|
||||
request->rawRequest = object;
|
||||
request->rpcRequest = wrapInLayer(object, getDatacenterWithId(datacenterId), request);
|
||||
auto cancelledIterator = tokensToBeCancelled.find(request->requestToken);
|
||||
if (cancelledIterator != tokensToBeCancelled.end()) {
|
||||
if (LOGS_ENABLED) DEBUG_D("(3) request is cancelled before sending, token %d", request->requestToken);
|
||||
tokensToBeCancelled.erase(cancelledIterator);
|
||||
delete request;
|
||||
return request->requestToken;
|
||||
}
|
||||
requestsQueue.push_back(std::unique_ptr<Request>(request));
|
||||
if (immediate) {
|
||||
processRequestQueue(0, 0);
|
||||
|
@ -1788,6 +1798,12 @@ int32_t ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onCompl
|
|||
auto request = new Request(instanceNum, requestToken, connetionType, flags, datacenterId, onComplete, onQuickAck, nullptr);
|
||||
request->rawRequest = object;
|
||||
request->rpcRequest = wrapInLayer(object, getDatacenterWithId(datacenterId), request);
|
||||
auto cancelledIterator = tokensToBeCancelled.find(request->requestToken);
|
||||
if (cancelledIterator != tokensToBeCancelled.end()) {
|
||||
if (LOGS_ENABLED) DEBUG_D("(1) request is cancelled before sending, token %d", requestToken);
|
||||
tokensToBeCancelled.erase(cancelledIterator);
|
||||
delete request;
|
||||
}
|
||||
requestsQueue.push_back(std::unique_ptr<Request>(request));
|
||||
if (immediate) {
|
||||
processRequestQueue(0, 0);
|
||||
|
@ -1807,14 +1823,17 @@ void ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onComplete
|
|||
exit(1);
|
||||
}
|
||||
if (ptr1 != nullptr) {
|
||||
DEBUG_DELREF("connectionsmanager ptr1");
|
||||
env->DeleteGlobalRef(ptr1);
|
||||
ptr1 = nullptr;
|
||||
}
|
||||
if (ptr2 != nullptr) {
|
||||
DEBUG_DELREF("connectionsmanager ptr2");
|
||||
env->DeleteGlobalRef(ptr2);
|
||||
ptr2 = nullptr;
|
||||
}
|
||||
if (ptr3 != nullptr) {
|
||||
DEBUG_DELREF("connectionsmanager ptr3");
|
||||
env->DeleteGlobalRef(ptr3);
|
||||
ptr3 = nullptr;
|
||||
}
|
||||
|
@ -1829,6 +1848,13 @@ void ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onComplete
|
|||
request->ptr3 = ptr3;
|
||||
request->rpcRequest = wrapInLayer(object, getDatacenterWithId(datacenterId), request);
|
||||
if (LOGS_ENABLED) DEBUG_D("send request wrapped %p - %s", request->rpcRequest.get(), typeid(*(request->rpcRequest.get())).name());
|
||||
auto cancelledIterator = tokensToBeCancelled.find(request->requestToken);
|
||||
if (cancelledIterator != tokensToBeCancelled.end()) {
|
||||
if (LOGS_ENABLED) DEBUG_D("(2) request is cancelled before sending, token %d", requestToken);
|
||||
tokensToBeCancelled.erase(cancelledIterator);
|
||||
delete request;
|
||||
return;
|
||||
}
|
||||
requestsQueue.push_back(std::unique_ptr<Request>(request));
|
||||
if (immediate) {
|
||||
processRequestQueue(0, 0);
|
||||
|
@ -1869,7 +1895,7 @@ void ConnectionsManager::bindRequestToGuid(int32_t requestToken, int32_t guid) {
|
|||
});
|
||||
}
|
||||
|
||||
void ConnectionsManager::setUserId(int32_t userId) {
|
||||
void ConnectionsManager::setUserId(int64_t userId) {
|
||||
scheduleTask([&, userId] {
|
||||
int32_t oldUserId = currentUserId;
|
||||
currentUserId = userId;
|
||||
|
@ -1890,16 +1916,19 @@ void ConnectionsManager::setUserId(int32_t userId) {
|
|||
}
|
||||
|
||||
void ConnectionsManager::switchBackend(bool restart) {
|
||||
//scheduleTask([&, restart] {
|
||||
scheduleTask([&, restart] {
|
||||
currentDatacenterId = 1;
|
||||
testBackend = !testBackend;
|
||||
if (!restart) {
|
||||
Handshake::cleanupServerKeys();
|
||||
}
|
||||
datacenters.clear();
|
||||
initDatacenters();
|
||||
saveConfig();
|
||||
if (restart) {
|
||||
exit(1);
|
||||
}
|
||||
//});
|
||||
});
|
||||
}
|
||||
|
||||
void ConnectionsManager::removeRequestFromGuid(int32_t requestToken) {
|
||||
|
@ -1920,6 +1949,10 @@ void ConnectionsManager::removeRequestFromGuid(int32_t requestToken) {
|
|||
}
|
||||
|
||||
bool ConnectionsManager::cancelRequestInternal(int32_t token, int64_t messageId, bool notifyServer, bool removeFromClass) {
|
||||
if (!tokensToBeCancelled.empty() && (connectionState != ConnectionStateWaitingForNetwork || tokensToBeCancelled.size() > 5000)) {
|
||||
tokensToBeCancelled.clear();
|
||||
}
|
||||
|
||||
for (auto iter = requestsQueue.begin(); iter != requestsQueue.end(); iter++) {
|
||||
Request *request = iter->get();
|
||||
if ((token != 0 && request->requestToken == token) || (messageId != 0 && request->respondsToMessageId(messageId))) {
|
||||
|
@ -1950,6 +1983,12 @@ bool ConnectionsManager::cancelRequestInternal(int32_t token, int64_t messageId,
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (token != 0 && connectionState == ConnectionStateWaitingForNetwork) {
|
||||
if (LOGS_ENABLED) DEBUG_D("request is tried to be cancelled, but it does not even exist, token %d", token);
|
||||
tokensToBeCancelled.insert(token);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2067,7 +2106,7 @@ void ConnectionsManager::requestSaltsForDatacenter(Datacenter *datacenter, bool
|
|||
requestingSaltsForDc.push_back(id);
|
||||
auto request = new TL_get_future_salts();
|
||||
request->num = 32;
|
||||
sendRequest(request, [&, datacenter, id, media](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
sendRequest(request, [&, datacenter, id, media](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
auto iter = std::find(requestingSaltsForDc.begin(), requestingSaltsForDc.end(), id);
|
||||
if (iter != requestingSaltsForDc.end()) {
|
||||
requestingSaltsForDc.erase(iter);
|
||||
|
@ -2102,7 +2141,7 @@ void ConnectionsManager::registerForInternalPushUpdates() {
|
|||
request->token_type = 7;
|
||||
request->token = to_string_uint64((uint64_t) pushSessionId);
|
||||
|
||||
sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
if (error == nullptr) {
|
||||
registeredForInternalPush = true;
|
||||
if (LOGS_ENABLED) DEBUG_D("registered for internal push");
|
||||
|
@ -2302,7 +2341,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
auto error = new TL_error();
|
||||
error->code = -123;
|
||||
error->text = "RETRY_LIMIT";
|
||||
request->onComplete(nullptr, error, connection->currentNetworkType, 0);
|
||||
request->onComplete(nullptr, error, connection->currentNetworkType, 0, request->messageId);
|
||||
delete error;
|
||||
iter = runningRequests.erase(iter);
|
||||
continue;
|
||||
|
@ -2503,7 +2542,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
} else {
|
||||
currentCount = 0;
|
||||
}
|
||||
if (!networkAvailable || currentCount >= 12) {
|
||||
if (!networkAvailable || currentCount >= 16) {
|
||||
iter++;
|
||||
continue;
|
||||
}
|
||||
|
@ -2825,16 +2864,22 @@ std::unique_ptr<TLObject> ConnectionsManager::wrapInLayer(TLObject *object, Data
|
|||
objectValue->key = "tz_offset";
|
||||
objectValue->value = std::unique_ptr<JSONValue>(jsonNumber);
|
||||
|
||||
if (currentPerformanceClass != -1) {
|
||||
objectValue = new TL_jsonObjectValue();
|
||||
jsonObject->value.push_back(std::unique_ptr<TL_jsonObjectValue>(objectValue));
|
||||
|
||||
auto jsonNumber = new TL_jsonNumber();
|
||||
jsonNumber->value = currentPerformanceClass + 1;
|
||||
objectValue->key = "perf_cat";
|
||||
objectValue->value = std::unique_ptr<JSONValue>(jsonNumber);
|
||||
}
|
||||
|
||||
request->flags |= 2;
|
||||
|
||||
if (!proxyAddress.empty() && !proxySecret.empty()) {
|
||||
request->flags |= 1;
|
||||
request->proxy = std::make_unique<TL_inputClientProxy>();
|
||||
if (proxyAddress == "127.0.0.1") {
|
||||
request->proxy->address = "neko.services";
|
||||
} else {
|
||||
request->proxy->address = proxyAddress;
|
||||
}
|
||||
request->proxy->port = proxyPort;
|
||||
}
|
||||
|
||||
|
@ -2963,7 +3008,7 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround) {
|
|||
}
|
||||
|
||||
auto request = new TL_help_getConfig();
|
||||
sendRequest(request, [&, workaround](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
sendRequest(request, [&, workaround](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
if ((!workaround && !updatingDcSettings) || (workaround && !updatingDcSettingsWorkaround)) {
|
||||
return;
|
||||
}
|
||||
|
@ -2985,12 +3030,14 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround) {
|
|||
std::vector<TcpAddress> addressesIpv4Download;
|
||||
std::vector<TcpAddress> addressesIpv6Download;
|
||||
bool isCdn = false;
|
||||
bool forceTryIpV6;
|
||||
|
||||
void addAddressAndPort(TL_dcOption *dcOption) {
|
||||
std::vector<TcpAddress> *addresses;
|
||||
if (!isCdn) {
|
||||
isCdn = dcOption->cdn;
|
||||
}
|
||||
forceTryIpV6 = dcOption->force_try_ipv6;
|
||||
if (dcOption->media_only) {
|
||||
if (dcOption->ipv6) {
|
||||
addresses = &addressesIpv6Download;
|
||||
|
@ -3013,9 +3060,14 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround) {
|
|||
if (dcOption->secret != nullptr) {
|
||||
secret = std::string((const char *) dcOption->secret->bytes, dcOption->secret->length);
|
||||
}
|
||||
if (LOGS_ENABLED) DEBUG_D("getConfig add %s:%d to dc%d, flags %d, has secret = %d[%d]", dcOption->ip_address.c_str(), dcOption->port, dcOption->id, dcOption->flags, dcOption->secret != nullptr ? 1 : 0, dcOption->secret != nullptr ? dcOption->secret->length : 0);
|
||||
if (LOGS_ENABLED) DEBUG_D("getConfig add %s:%d to dc%d, flags %d, has_secret = %d[%d], try_this_port_only = %d, force_try_ipv6 = %d", dcOption->ip_address.c_str(), dcOption->port, dcOption->id, dcOption->flags, dcOption->secret != nullptr ? 1 : 0, dcOption->secret != nullptr ? dcOption->secret->length : 0, dcOption->thisPortOnly ? 1 : 0, dcOption->force_try_ipv6 ? 1 : 0);
|
||||
if (dcOption->thisPortOnly) {
|
||||
addresses->insert(addresses->begin(), TcpAddress(dcOption->ip_address, dcOption->port, dcOption->flags, secret));
|
||||
} else {
|
||||
addresses->push_back(TcpAddress(dcOption->ip_address, dcOption->port, dcOption->flags, secret));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::unique_ptr<DatacenterInfo>> map;
|
||||
|
@ -3078,7 +3130,7 @@ void ConnectionsManager::moveToDatacenter(uint32_t datacenterId) {
|
|||
if (currentUserId) {
|
||||
auto request = new TL_auth_exportAuthorization();
|
||||
request->dc_id = datacenterId;
|
||||
sendRequest(request, [&, datacenterId](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
sendRequest(request, [&, datacenterId](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
if (error == nullptr) {
|
||||
movingAuthorization = std::move(((TL_auth_exportedAuthorization *) response)->bytes);
|
||||
authorizeOnMovingDatacenter();
|
||||
|
@ -3110,7 +3162,7 @@ void ConnectionsManager::authorizeOnMovingDatacenter() {
|
|||
auto request = new TL_auth_importAuthorization();
|
||||
request->id = currentUserId;
|
||||
request->bytes = std::move(movingAuthorization);
|
||||
sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
if (error == nullptr) {
|
||||
authorizedOnMovingDatacenter();
|
||||
} else {
|
||||
|
@ -3269,7 +3321,7 @@ void ConnectionsManager::applyDnsConfig(NativeByteBuffer *buffer, std::string ph
|
|||
});
|
||||
}
|
||||
|
||||
void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerpting, std::string installerId, std::string packageId, int32_t timezoneOffset, int32_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType) {
|
||||
void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerpting, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass) {
|
||||
currentVersion = version;
|
||||
currentLayer = layer;
|
||||
currentApiId = apiId;
|
||||
|
@ -3289,6 +3341,7 @@ void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, st
|
|||
pushConnectionEnabled = enablePushConnection;
|
||||
currentNetworkType = networkType;
|
||||
networkAvailable = hasNetwork;
|
||||
currentPerformanceClass = performanceClass;
|
||||
if (isPaused) {
|
||||
lastPauseTime = getCurrentTimeMonotonicMillis();
|
||||
}
|
||||
|
@ -3557,6 +3610,7 @@ void ConnectionsManager::useJavaVM(JavaVM *vm, bool useJavaByteBuffers) {
|
|||
if (LOGS_ENABLED) DEBUG_E("can't get jnienv");
|
||||
exit(1);
|
||||
}
|
||||
DEBUG_REF("connectionsmanager byte buffer");
|
||||
jclass_ByteBuffer = (jclass) env->NewGlobalRef(env->FindClass("java/nio/ByteBuffer"));
|
||||
if (jclass_ByteBuffer == nullptr) {
|
||||
if (LOGS_ENABLED) DEBUG_E("can't find java ByteBuffer class");
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <sys/epoll.h>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
#include "Defines.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
|
@ -57,20 +58,13 @@ public:
|
|||
void applyDatacenterAddress(uint32_t datacenterId, std::string ipAddress, uint32_t port);
|
||||
void setDelegate(ConnectiosManagerDelegate *connectiosManagerDelegate);
|
||||
ConnectionState getConnectionState();
|
||||
void setUserId(int32_t userId);
|
||||
void setUserId(int64_t userId);
|
||||
void switchBackend(bool restart);
|
||||
void resumeNetwork(bool partial);
|
||||
void pauseNetwork();
|
||||
void setNetworkAvailable(bool value, int32_t type, bool slow);
|
||||
|
||||
void setIpStrategy(uint8_t value);
|
||||
|
||||
void init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel,
|
||||
std::string systemVersion, std::string appVersion, std::string langCode,
|
||||
std::string systemLangCode, std::string configPath, std::string logPath,
|
||||
std::string regId, std::string cFingerprint, std::string installerId,
|
||||
std::string packageId, int32_t timezoneOffset, int32_t userId, bool isPaused, bool enablePushConnection,
|
||||
bool hasNetwork, int32_t networkType);
|
||||
void init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerprint, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass);
|
||||
void setProxySettings(std::string address, uint16_t port, std::string username, std::string password, std::string secret);
|
||||
void setLangCode(std::string langCode);
|
||||
void setRegId(std::string regId);
|
||||
|
@ -129,11 +123,8 @@ private:
|
|||
TLObject *getRequestWithMessageId(int64_t messageId);
|
||||
void onDatacenterHandshakeComplete(Datacenter *datacenter, HandshakeType type, int32_t timeDiff);
|
||||
void onDatacenterExportAuthorizationComplete(Datacenter *datacenter);
|
||||
|
||||
int64_t generateMessageId();
|
||||
|
||||
uint8_t getIpStratagy();
|
||||
|
||||
bool isNetworkAvailable();
|
||||
|
||||
void scheduleCheckProxyInternal(ProxyCheckInfo *proxyCheckInfo);
|
||||
|
@ -215,6 +206,7 @@ private:
|
|||
requestsList requestsQueue;
|
||||
requestsList runningRequests;
|
||||
std::vector<uint32_t> requestingSaltsForDc;
|
||||
std::unordered_set<int32_t> tokensToBeCancelled;
|
||||
int32_t lastPingId = 0;
|
||||
int64_t lastInvokeAfterMessageId = 0;
|
||||
|
||||
|
@ -234,9 +226,10 @@ private:
|
|||
std::string currentSystemLangCode;
|
||||
std::string currentConfigPath;
|
||||
std::string currentLogPath;
|
||||
int32_t currentUserId = 0;
|
||||
int64_t currentUserId = 0;
|
||||
bool registeredForInternalPush = false;
|
||||
bool pushConnectionEnabled = true;
|
||||
int32_t currentPerformanceClass = -1;
|
||||
|
||||
std::map<uint32_t, std::vector<std::unique_ptr<NetworkMessage>>> genericMessagesToDatacenters;
|
||||
std::map<uint32_t, std::vector<std::unique_ptr<NetworkMessage>>> genericMediaMessagesToDatacenters;
|
||||
|
@ -259,7 +252,6 @@ private:
|
|||
friend class Config;
|
||||
friend class FileLog;
|
||||
friend class Handshake;
|
||||
|
||||
};
|
||||
|
||||
#ifdef ANDROID
|
||||
|
|
|
@ -414,6 +414,7 @@ void Datacenter::nextAddressOrPort(uint32_t flags) {
|
|||
if (currentAddressNum + 1 < addresses->size()) {
|
||||
currentAddressNum++;
|
||||
} else {
|
||||
repeatCheckingAddresses = true;
|
||||
currentAddressNum = 0;
|
||||
}
|
||||
currentPortNum = 0;
|
||||
|
@ -1434,14 +1435,14 @@ void Datacenter::exportAuthorization() {
|
|||
auto request = new TL_auth_exportAuthorization();
|
||||
request->dc_id = datacenterId;
|
||||
if (LOGS_ENABLED) DEBUG_D("dc%u begin export authorization", datacenterId);
|
||||
ConnectionsManager::getInstance(instanceNum).sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
ConnectionsManager::getInstance(instanceNum).sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
if (error == nullptr) {
|
||||
auto res = (TL_auth_exportedAuthorization *) response;
|
||||
auto request2 = new TL_auth_importAuthorization();
|
||||
request2->bytes = std::move(res->bytes);
|
||||
request2->id = res->id;
|
||||
if (LOGS_ENABLED) DEBUG_D("dc%u begin import authorization", datacenterId);
|
||||
ConnectionsManager::getInstance(instanceNum).sendRequest(request2, [&](TLObject *response2, TL_error *error2, int32_t networkType, int64_t responseTime) {
|
||||
ConnectionsManager::getInstance(instanceNum).sendRequest(request2, [&](TLObject *response2, TL_error *error2, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
if (error2 == nullptr) {
|
||||
authorized = true;
|
||||
ConnectionsManager::getInstance(instanceNum).onDatacenterExportAuthorizationComplete(this);
|
||||
|
@ -1477,6 +1478,12 @@ void Datacenter::resetInitVersion() {
|
|||
lastInitMediaVersion = 0;
|
||||
}
|
||||
|
||||
bool Datacenter::isRepeatCheckingAddresses() {
|
||||
bool b = repeatCheckingAddresses;
|
||||
repeatCheckingAddresses = false;
|
||||
return b;
|
||||
}
|
||||
|
||||
TL_help_configSimple *Datacenter::decodeSimpleConfig(NativeByteBuffer *buffer) {
|
||||
TL_help_configSimple *result = nullptr;
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
bool isExportingAuthorization();
|
||||
bool hasMediaAddress();
|
||||
void resetInitVersion();
|
||||
bool isRepeatCheckingAddresses();
|
||||
|
||||
Connection *getDownloadConnection(uint8_t num, bool create);
|
||||
Connection *getProxyConnection(uint8_t num, bool create, bool connect);
|
||||
|
@ -121,6 +122,7 @@ private:
|
|||
int64_t authKeyMediaTempId = 0;
|
||||
Config *config = nullptr;
|
||||
bool isCdnDatacenter = false;
|
||||
bool repeatCheckingAddresses = false;
|
||||
|
||||
std::vector<std::unique_ptr<Handshake>> handshakes;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "ByteArray.h"
|
||||
|
||||
#define USE_DEBUG_SESSION false
|
||||
#define READ_BUFFER_SIZE 1024 * 128
|
||||
#define READ_BUFFER_SIZE 1024 * 1024 * 2
|
||||
//#define DEBUG_VERSION
|
||||
#define PFS_ENABLED 1
|
||||
#define DEFAULT_DATACENTER_ID INT_MAX
|
||||
|
@ -27,7 +27,7 @@
|
|||
#define DOWNLOAD_CONNECTIONS_COUNT 2
|
||||
#define UPLOAD_CONNECTIONS_COUNT 4
|
||||
#define CONNECTION_BACKGROUND_KEEP_TIME 10000
|
||||
#define MAX_ACCOUNT_COUNT 16
|
||||
//#define MAX_ACCOUNT_COUNT 16
|
||||
#define USE_DELEGATE_HOST_RESOLVE
|
||||
|
||||
#define USE_IPV4_ONLY 0
|
||||
|
@ -39,19 +39,15 @@
|
|||
#define NETWORK_TYPE_ROAMING 2
|
||||
|
||||
class TLObject;
|
||||
|
||||
class TL_error;
|
||||
|
||||
class Request;
|
||||
|
||||
class TL_message;
|
||||
|
||||
class TL_config;
|
||||
class NativeByteBuffer;
|
||||
class Handshake;
|
||||
class ConnectionSocket;
|
||||
|
||||
typedef std::function<void(TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime)> onCompleteFunc;
|
||||
typedef std::function<void(TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId)> onCompleteFunc;
|
||||
typedef std::function<void()> onQuickAckFunc;
|
||||
typedef std::function<void()> onWriteToSocketFunc;
|
||||
typedef std::function<void(int64_t messageId)> fillParamsFunc;
|
||||
|
@ -173,7 +169,8 @@ enum RequestFlag {
|
|||
RequestFlagInvokeAfter = 64,
|
||||
RequestFlagNeedQuickAck = 128,
|
||||
RequestFlagUseUnboundKey = 256,
|
||||
RequestFlagResendAfter = 512
|
||||
RequestFlagResendAfter = 512,
|
||||
RequestFlagIgnoreFloodWait = 1024
|
||||
};
|
||||
|
||||
inline std::string to_string_int32(int32_t value) {
|
||||
|
|
|
@ -22,6 +22,8 @@ bool LOGS_ENABLED = true;
|
|||
bool LOGS_ENABLED = false;
|
||||
#endif
|
||||
|
||||
bool REF_LOGS_ENABLED = false;
|
||||
|
||||
FileLog &FileLog::getInstance() {
|
||||
static FileLog instance;
|
||||
return instance;
|
||||
|
@ -39,6 +41,41 @@ void FileLog::init(std::string path) {
|
|||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
void FileLog::fatal(const char *message, ...) {
|
||||
if (!LOGS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
va_list argptr;
|
||||
va_start(argptr, message);
|
||||
time_t t = time(0);
|
||||
struct tm *now = localtime(&t);
|
||||
#ifdef ANDROID
|
||||
__android_log_vprint(ANDROID_LOG_FATAL, "tgnet", message, argptr);
|
||||
va_end(argptr);
|
||||
va_start(argptr, message);
|
||||
#else
|
||||
printf("%d-%d %02d:%02d:%02d FATAL ERROR: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
vprintf(message, argptr);
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
va_end(argptr);
|
||||
va_start(argptr, message);
|
||||
#endif
|
||||
FILE *logFile = getInstance().logFile;
|
||||
if (logFile) {
|
||||
fprintf(logFile, "%d-%d %02d:%02d:%02d FATAL ERROR: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
|
||||
vfprintf(logFile, message, argptr);
|
||||
fprintf(logFile, "\n");
|
||||
fflush(logFile);
|
||||
}
|
||||
|
||||
va_end(argptr);
|
||||
|
||||
#ifdef DEBUG_VERSION
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileLog::e(const char *message, ...) {
|
||||
if (!LOGS_ENABLED) {
|
||||
return;
|
||||
|
@ -131,3 +168,43 @@ void FileLog::d(const char *message, ...) {
|
|||
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
static int refsCount = 0;
|
||||
|
||||
void FileLog::ref(const char *message, ...) {
|
||||
if (!REF_LOGS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
va_list argptr;
|
||||
va_start(argptr, message);
|
||||
time_t t = time(0);
|
||||
struct tm *now = localtime(&t);
|
||||
refsCount++;
|
||||
#ifdef ANDROID
|
||||
std::ostringstream s;
|
||||
s << refsCount << " refs (+ref): " << message;
|
||||
__android_log_vprint(ANDROID_LOG_VERBOSE, "tgnetREF", s.str().c_str(), argptr);
|
||||
va_end(argptr);
|
||||
va_start(argptr, message);
|
||||
#endif
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void FileLog::delref(const char *message, ...) {
|
||||
if (!REF_LOGS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
va_list argptr;
|
||||
va_start(argptr, message);
|
||||
time_t t = time(0);
|
||||
struct tm *now = localtime(&t);
|
||||
refsCount--;
|
||||
#ifdef ANDROID
|
||||
std::ostringstream s;
|
||||
s << refsCount << " refs (-ref): " << message;
|
||||
__android_log_vprint(ANDROID_LOG_VERBOSE, "tgnetREF", s.str().c_str(), argptr);
|
||||
va_end(argptr);
|
||||
va_start(argptr, message);
|
||||
#endif
|
||||
va_end(argptr);
|
||||
}
|
|
@ -15,9 +15,12 @@ class FileLog {
|
|||
public:
|
||||
FileLog();
|
||||
void init(std::string path);
|
||||
static void fatal(const char *message, ...) __attribute__((format (printf, 1, 2)));
|
||||
static void e(const char *message, ...) __attribute__((format (printf, 1, 2)));
|
||||
static void w(const char *message, ...) __attribute__((format (printf, 1, 2)));
|
||||
static void d(const char *message, ...) __attribute__((format (printf, 1, 2)));
|
||||
static void ref(const char *message, ...) __attribute__((format (printf, 1, 2)));
|
||||
static void delref(const char *message, ...) __attribute__((format (printf, 1, 2)));
|
||||
|
||||
static FileLog &getInstance();
|
||||
|
||||
|
@ -28,8 +31,12 @@ private:
|
|||
|
||||
extern bool LOGS_ENABLED;
|
||||
|
||||
#define DEBUG_FATAL FileLog::getInstance().fatal
|
||||
#define DEBUG_E FileLog::getInstance().e
|
||||
#define DEBUG_W FileLog::getInstance().w
|
||||
#define DEBUG_D FileLog::getInstance().d
|
||||
|
||||
#define DEBUG_REF FileLog::getInstance().ref
|
||||
#define DEBUG_DELREF FileLog::getInstance().delref
|
||||
|
||||
#endif
|
||||
|
|
|
@ -190,7 +190,7 @@ inline bool factorizeValue(uint64_t what, uint32_t &p, uint32_t &q) {
|
|||
}
|
||||
return true;
|
||||
} else {
|
||||
if (LOGS_ENABLED) DEBUG_E("factorization failed for %" PRIu64, what);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("factorization failed for %" PRIu64, what);
|
||||
p = 0;
|
||||
q = 0;
|
||||
return false;
|
||||
|
@ -200,7 +200,7 @@ inline bool factorizeValue(uint64_t what, uint32_t &p, uint32_t &q) {
|
|||
inline bool check_prime(BIGNUM *p) {
|
||||
int result = 0;
|
||||
if (!BN_primality_test(&result, p, 64, bnContext, 0, NULL)) {
|
||||
if (LOGS_ENABLED) DEBUG_E("OpenSSL error at BN_primality_test");
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("OpenSSL error at BN_primality_test");
|
||||
return false;
|
||||
}
|
||||
return result != 0;
|
||||
|
@ -215,20 +215,20 @@ inline bool isGoodPrime(BIGNUM *p, uint32_t g) {
|
|||
BIGNUM *dh_g = BN_new();
|
||||
|
||||
if (!BN_set_word(dh_g, 4 * g)) {
|
||||
if (LOGS_ENABLED) DEBUG_E("OpenSSL error at BN_set_word(dh_g, 4 * g)");
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("OpenSSL error at BN_set_word(dh_g, 4 * g)");
|
||||
BN_free(t);
|
||||
BN_free(dh_g);
|
||||
return false;
|
||||
}
|
||||
if (!BN_mod(t, p, dh_g, bnContext)) {
|
||||
if (LOGS_ENABLED) DEBUG_E("OpenSSL error at BN_mod");
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("OpenSSL error at BN_mod");
|
||||
BN_free(t);
|
||||
BN_free(dh_g);
|
||||
return false;
|
||||
}
|
||||
uint64_t x = BN_get_word(t);
|
||||
if (x >= 4 * g) {
|
||||
if (LOGS_ENABLED) DEBUG_E("OpenSSL error at BN_get_word");
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("OpenSSL error at BN_get_word");
|
||||
BN_free(t);
|
||||
BN_free(dh_g);
|
||||
return false;
|
||||
|
@ -316,6 +316,11 @@ inline bool isGoodGaAndGb(BIGNUM *g_a, BIGNUM *p) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Handshake::cleanupServerKeys() {
|
||||
serverPublicKeys.clear();
|
||||
serverPublicKeysFingerprints.clear();
|
||||
}
|
||||
|
||||
void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
|
||||
if (handshakeState == 0) {
|
||||
return;
|
||||
|
@ -390,6 +395,8 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
|
|||
loadCdnConfig(currentDatacenter);
|
||||
} else {
|
||||
if (LOGS_ENABLED) DEBUG_E("account%u dc%u handshake: can't find valid server public key, type = %d", currentDatacenter->instanceNum, currentDatacenter->datacenterId, handshakeType);
|
||||
serverPublicKeys.clear();
|
||||
serverPublicKeysFingerprints.clear();
|
||||
beginHandshake(false);
|
||||
}
|
||||
return;
|
||||
|
@ -848,7 +855,7 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
|
|||
request->encrypted_message = currentDatacenter->createRequestsData(array, nullptr, connection, true);
|
||||
};
|
||||
|
||||
authKeyPendingRequestId = ConnectionsManager::getInstance(currentDatacenter->instanceNum).sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
authKeyPendingRequestId = ConnectionsManager::getInstance(currentDatacenter->instanceNum).sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
authKeyPendingMessageId = 0;
|
||||
authKeyPendingRequestId = 0;
|
||||
if (response != nullptr && typeid(*response) == typeid(TL_boolTrue)) {
|
||||
|
@ -895,7 +902,8 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
|
|||
}
|
||||
|
||||
void Handshake::sendAckRequest(int64_t messageId) {
|
||||
/*auto msgsAck = new TL_msgs_ack();
|
||||
/*
|
||||
auto msgsAck = new TL_msgs_ack();
|
||||
msgsAck->msg_ids.push_back(messageId);
|
||||
sendRequestData(msgsAck, false);*/
|
||||
}
|
||||
|
@ -964,7 +972,7 @@ void Handshake::loadCdnConfig(Datacenter *datacenter) {
|
|||
loadingCdnKeys = true;
|
||||
auto request = new TL_help_getCdnConfig();
|
||||
|
||||
ConnectionsManager::getInstance(datacenter->instanceNum).sendRequest(request, [&, datacenter](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
ConnectionsManager::getInstance(datacenter->instanceNum).sendRequest(request, [&, datacenter](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) {
|
||||
if (response != nullptr) {
|
||||
auto config = (TL_cdnConfig *) response;
|
||||
size_t count = config->public_keys.size();
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
void processHandshakeResponse(TLObject *message, int64_t messageId);
|
||||
void onHandshakeConnectionConnected();
|
||||
void onHandshakeConnectionClosed();
|
||||
static void cleanupServerKeys();
|
||||
HandshakeType getType();
|
||||
ByteArray *getPendingAuthKey();
|
||||
int64_t getPendingAuthKeyId();
|
||||
|
|
|
@ -111,7 +111,7 @@ void TL_api_response::readParamsEx(NativeByteBuffer *stream, uint32_t bytes, boo
|
|||
TL_future_salt *TL_future_salt::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_future_salt::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_future_salt", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_future_salt", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_future_salt *result = new TL_future_salt();
|
||||
|
@ -128,7 +128,7 @@ void TL_future_salt::readParams(NativeByteBuffer *stream, int32_t instanceNum, b
|
|||
TL_msgs_state_info *TL_msgs_state_info::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_msgs_state_info::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_msgs_state_info", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_msgs_state_info", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_msgs_state_info *result = new TL_msgs_state_info();
|
||||
|
@ -158,7 +158,7 @@ Server_DH_Params *Server_DH_Params::TLdeserialize(NativeByteBuffer *stream, uint
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in Server_DH_Params", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in Server_DH_Params", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -180,7 +180,7 @@ void TL_server_DH_params_ok::readParams(NativeByteBuffer *stream, int32_t instan
|
|||
TL_resPQ *TL_resPQ::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_resPQ::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_resPQ", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_resPQ", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_resPQ *result = new TL_resPQ();
|
||||
|
@ -195,7 +195,7 @@ void TL_resPQ::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &e
|
|||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_resPQ, got %x", magic);
|
||||
return;
|
||||
}
|
||||
uint32_t count = stream->readUint32(&error);
|
||||
|
@ -297,7 +297,7 @@ void TL_auth_dropTempAuthKeys::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_pong *TL_pong::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_pong::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_pong", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_pong", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_pong *result = new TL_pong();
|
||||
|
@ -313,7 +313,7 @@ void TL_pong::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
|
|||
TL_future_salts *TL_future_salts::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_future_salts::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_future_salts", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_future_salts", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_future_salts *result = new TL_future_salts();
|
||||
|
@ -349,7 +349,7 @@ RpcDropAnswer *RpcDropAnswer::TLdeserialize(NativeByteBuffer *stream, uint32_t c
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in RpcDropAnswer", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in RpcDropAnswer", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -384,7 +384,7 @@ Set_client_DH_params_answer *Set_client_DH_params_answer::TLdeserialize(NativeBy
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in Set_client_DH_params_answer", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in Set_client_DH_params_answer", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -420,7 +420,7 @@ BadMsgNotification *BadMsgNotification::TLdeserialize(NativeByteBuffer *stream,
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in BadMsgNotification", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in BadMsgNotification", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -443,7 +443,7 @@ void TL_bad_server_salt::readParams(NativeByteBuffer *stream, int32_t instanceNu
|
|||
TL_msgs_state_req *TL_msgs_state_req::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_msgs_state_req::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_msgs_state_req", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_msgs_state_req", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_msgs_state_req *result = new TL_msgs_state_req();
|
||||
|
@ -455,7 +455,7 @@ void TL_msgs_state_req::readParams(NativeByteBuffer *stream, int32_t instanceNum
|
|||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_msgs_state_req, got %x", magic);
|
||||
return;
|
||||
}
|
||||
uint32_t count = stream->readUint32(&error);
|
||||
|
@ -489,7 +489,7 @@ MsgDetailedInfo *MsgDetailedInfo::TLdeserialize(NativeByteBuffer *stream, uint32
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in MsgDetailedInfo", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in MsgDetailedInfo", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -512,7 +512,7 @@ void TL_msg_detailed_info::readParams(NativeByteBuffer *stream, int32_t instance
|
|||
TL_msg_copy *TL_msg_copy::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_msg_copy::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_msg_copy", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_msg_copy", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_msg_copy *result = new TL_msg_copy();
|
||||
|
@ -532,7 +532,7 @@ void TL_msg_copy::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_msgs_all_info *TL_msgs_all_info::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_msgs_all_info::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_msgs_all_info", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_msgs_all_info", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_msgs_all_info *result = new TL_msgs_all_info();
|
||||
|
@ -544,7 +544,7 @@ void TL_msgs_all_info::readParams(NativeByteBuffer *stream, int32_t instanceNum,
|
|||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_msgs_all_info, got %x", magic);
|
||||
return;
|
||||
}
|
||||
uint32_t count = stream->readUint32(&error);
|
||||
|
@ -586,7 +586,7 @@ DestroySessionRes *DestroySessionRes::TLdeserialize(NativeByteBuffer *stream, ui
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in DestroySessionRes", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in DestroySessionRes", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -604,7 +604,7 @@ void TL_destroy_session_none::readParams(NativeByteBuffer *stream, int32_t insta
|
|||
TL_msgs_ack *TL_msgs_ack::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_msgs_ack::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_msgs_ack", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_msgs_ack", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_msgs_ack *result = new TL_msgs_ack();
|
||||
|
@ -616,7 +616,7 @@ void TL_msgs_ack::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool
|
|||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_msgs_ack, got %x", magic);
|
||||
return;
|
||||
}
|
||||
uint32_t count = stream->readUint32(&error);
|
||||
|
@ -642,7 +642,7 @@ void TL_msgs_ack::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_msg_container *TL_msg_container::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_msg_container::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_msg_container", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_msg_container", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_msg_container *result = new TL_msg_container();
|
||||
|
@ -674,7 +674,7 @@ void TL_msg_container::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_message *TL_message::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_message::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_message", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_message", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_message *result = new TL_message();
|
||||
|
@ -709,7 +709,7 @@ void TL_message::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_msg_resend_req *TL_msg_resend_req::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_msg_resend_req::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_msg_resend_req", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_msg_resend_req", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_msg_resend_req *result = new TL_msg_resend_req();
|
||||
|
@ -721,7 +721,7 @@ void TL_msg_resend_req::readParams(NativeByteBuffer *stream, int32_t instanceNum
|
|||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_msg_resend_req, got %x", magic);
|
||||
return;
|
||||
}
|
||||
uint32_t count = stream->readUint32(&error);
|
||||
|
@ -771,7 +771,7 @@ void TL_client_DH_inner_data::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_server_DH_inner_data *TL_server_DH_inner_data::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_server_DH_inner_data::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_server_DH_inner_data", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_server_DH_inner_data", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_server_DH_inner_data *result = new TL_server_DH_inner_data();
|
||||
|
@ -906,7 +906,7 @@ void TL_gzip_packed::serializeToStream(NativeByteBuffer *stream) {
|
|||
TL_error *TL_error::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_error::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_error", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_error", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_error *result = new TL_error();
|
||||
|
@ -971,7 +971,7 @@ JSONValue *JSONValue::TLdeserialize(NativeByteBuffer *stream, uint32_t construct
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in JSONValue", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in JSONValue", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -981,7 +981,7 @@ JSONValue *JSONValue::TLdeserialize(NativeByteBuffer *stream, uint32_t construct
|
|||
TL_jsonObjectValue *TL_jsonObjectValue::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_jsonObjectValue::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_jsonObjectValue", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_jsonObjectValue", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_jsonObjectValue *result = new TL_jsonObjectValue();
|
||||
|
@ -1028,7 +1028,7 @@ void TL_jsonArray::readParams(NativeByteBuffer *stream, int32_t instanceNum, boo
|
|||
int magic = stream->readInt32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_jsonArray, got %x", magic);
|
||||
return;
|
||||
}
|
||||
int count = stream->readInt32(&error);
|
||||
|
@ -1055,7 +1055,7 @@ void TL_jsonObject::readParams(NativeByteBuffer *stream, int32_t instanceNum, bo
|
|||
int magic = stream->readInt32(&error);
|
||||
if (magic != 0x1cb5c415) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("wrong Vector magic, got %x", magic);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("wrong Vector magic in TL_jsonObject, got %x", magic);
|
||||
return;
|
||||
}
|
||||
int count = stream->readInt32(&error);
|
||||
|
@ -1117,7 +1117,7 @@ IpPort *IpPort::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, in
|
|||
break;
|
||||
default:
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in IpPort", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in IpPort", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
result->readParams(stream, instanceNum, error);
|
||||
|
@ -1155,7 +1155,7 @@ void TL_accessPointRule::readParams(NativeByteBuffer *stream, int32_t instanceNu
|
|||
TL_help_configSimple *TL_help_configSimple::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
|
||||
if (TL_help_configSimple::constructor != constructor) {
|
||||
error = true;
|
||||
if (LOGS_ENABLED) DEBUG_E("can't parse magic %x in TL_help_configSimple", constructor);
|
||||
if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in TL_help_configSimple", constructor);
|
||||
return nullptr;
|
||||
}
|
||||
TL_help_configSimple *result = new TL_help_configSimple();
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "ConnectionsManager.h"
|
||||
#include "BuffersStorage.h"
|
||||
|
||||
static int buffersCount = 0;
|
||||
|
||||
NativeByteBuffer::NativeByteBuffer(uint32_t size) {
|
||||
#ifdef ANDROID
|
||||
if (jclass_ByteBuffer != nullptr) {
|
||||
|
@ -27,6 +29,7 @@ NativeByteBuffer::NativeByteBuffer(uint32_t size) {
|
|||
if (LOGS_ENABLED) DEBUG_E("can't create javaByteBuffer");
|
||||
exit(1);
|
||||
}
|
||||
DEBUG_REF("nativebytebuffer");
|
||||
jobject globalRef = env->NewGlobalRef(javaByteBuffer);
|
||||
env->DeleteLocalRef(javaByteBuffer);
|
||||
javaByteBuffer = globalRef;
|
||||
|
@ -43,6 +46,7 @@ NativeByteBuffer::NativeByteBuffer(uint32_t size) {
|
|||
if (LOGS_ENABLED) DEBUG_E("can't allocate NativeByteBuffer buffer");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_limit = _capacity = size;
|
||||
}
|
||||
|
||||
|
@ -64,6 +68,7 @@ NativeByteBuffer::~NativeByteBuffer() {
|
|||
if (LOGS_ENABLED) DEBUG_E("can't get jnienv");
|
||||
exit(1);
|
||||
}
|
||||
DEBUG_DELREF("nativebytebuffer");
|
||||
env->DeleteGlobalRef(javaByteBuffer);
|
||||
javaByteBuffer = nullptr;
|
||||
}
|
||||
|
@ -694,6 +699,7 @@ jobject NativeByteBuffer::getJavaByteBuffer() {
|
|||
if (LOGS_ENABLED) DEBUG_E("can't allocate NativeByteBuffer buffer");
|
||||
exit(1);
|
||||
}
|
||||
DEBUG_REF("nativebytebuffer");
|
||||
jobject globalRef = env->NewGlobalRef(javaByteBuffer);
|
||||
env->DeleteLocalRef(javaByteBuffer);
|
||||
javaByteBuffer = globalRef;
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
|
||||
#include "ProxyCheckInfo.h"
|
||||
#include "ConnectionsManager.h"
|
||||
#include "FileLog.h"
|
||||
|
||||
ProxyCheckInfo::~ProxyCheckInfo() {
|
||||
#ifdef ANDROID
|
||||
if (ptr1 != nullptr) {
|
||||
DEBUG_DELREF("tgnet (2) request ptr1");
|
||||
jniEnv[instanceNum]->DeleteGlobalRef(ptr1);
|
||||
ptr1 = nullptr;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "ConnectionsManager.h"
|
||||
#include "Datacenter.h"
|
||||
#include "Connection.h"
|
||||
#include "FileLog.h"
|
||||
|
||||
Request::Request(int32_t instance, int32_t token, ConnectionType type, uint32_t flags, uint32_t datacenter, onCompleteFunc completeFunc, onQuickAckFunc quickAckFunc, onWriteToSocketFunc writeToSocketFunc) {
|
||||
requestToken = token;
|
||||
|
@ -29,14 +30,17 @@ Request::Request(int32_t instance, int32_t token, ConnectionType type, uint32_t
|
|||
Request::~Request() {
|
||||
#ifdef ANDROID
|
||||
if (ptr1 != nullptr) {
|
||||
DEBUG_DELREF("tgnet request ptr1");
|
||||
jniEnv[instanceNum]->DeleteGlobalRef(ptr1);
|
||||
ptr1 = nullptr;
|
||||
}
|
||||
if (ptr2 != nullptr) {
|
||||
DEBUG_DELREF("tgnet request ptr2");
|
||||
jniEnv[instanceNum]->DeleteGlobalRef(ptr2);
|
||||
ptr2 = nullptr;
|
||||
}
|
||||
if (ptr3 != nullptr) {
|
||||
DEBUG_DELREF("tgnet request ptr3");
|
||||
jniEnv[instanceNum]->DeleteGlobalRef(ptr3);
|
||||
ptr3 = nullptr;
|
||||
}
|
||||
|
@ -61,9 +65,9 @@ void Request::clear(bool time) {
|
|||
}
|
||||
}
|
||||
|
||||
void Request::onComplete(TLObject *result, TL_error *error, int32_t networkType, int64_t responseTime) {
|
||||
void Request::onComplete(TLObject *result, TL_error *error, int32_t networkType, int64_t responseTime, int64_t requestMsgId) {
|
||||
if (onCompleteRequestCallback != nullptr && (result != nullptr || error != nullptr)) {
|
||||
onCompleteRequestCallback(result, error, networkType, responseTime);
|
||||
onCompleteRequestCallback(result, error, networkType, responseTime, requestMsgId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
void addRespondMessageId(int64_t id);
|
||||
bool respondsToMessageId(int64_t id);
|
||||
void clear(bool time);
|
||||
void onComplete(TLObject *result, TL_error *error, int32_t networkType, int64_t responseTime);
|
||||
void onComplete(TLObject *result, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msg_id);
|
||||
void onQuickAck();
|
||||
void onWriteToSocket();
|
||||
bool isMediaRequest();
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
# 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/handler/exception_handler.cc \
|
||||
src/client/linux/handler/minidump_descriptor.cc \
|
||||
src/client/linux/log/log.cc \
|
||||
src/client/linux/dump_writer_common/thread_info.cc \
|
||||
src/client/linux/dump_writer_common/seccomp_unwinder.cc \
|
||||
src/client/linux/dump_writer_common/ucontext_reader.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.
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2009, 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 BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
#define BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/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__
|
53
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/client_info.h
vendored
Normal file
53
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/client_info.h
vendored
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
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/crash_generation_client.cc
vendored
Normal file
105
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/crash_generation_client.cc
vendored
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
|
|
@ -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_
|
330
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/crash_generation_server.cc
vendored
Normal file
330
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/crash_generation_server.cc
vendored
Normal file
|
@ -0,0 +1,330 @@
|
|||
// 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);
|
||||
|
||||
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
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/crash_generation_server.h
vendored
Normal file
135
TMessagesProj/jni/third_party/breakpad/src/client/linux/crash_generation/crash_generation_server.h
vendored
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_
|
3
TMessagesProj/jni/third_party/breakpad/src/client/linux/data/linux-gate-amd.sym
vendored
Normal file
3
TMessagesProj/jni/third_party/breakpad/src/client/linux/data/linux-gate-amd.sym
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so
|
||||
PUBLIC 400 0 __kernel_vsyscall
|
||||
STACK WIN 4 400 100 1 1 0 0 0 0 0 1
|
3
TMessagesProj/jni/third_party/breakpad/src/client/linux/data/linux-gate-intel.sym
vendored
Normal file
3
TMessagesProj/jni/third_party/breakpad/src/client/linux/data/linux-gate-intel.sym
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so
|
||||
PUBLIC 400 0 __kernel_vsyscall
|
||||
STACK WIN 4 400 200 3 3 0 0 0 0 0 1
|
61
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/mapping_info.h
vendored
Normal file
61
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/mapping_info.h
vendored
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
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h
vendored
Normal file
53
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h
vendored
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
|
154
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/seccomp_unwinder.cc
vendored
Normal file
154
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/seccomp_unwinder.cc
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
// 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/seccomp_unwinder.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
void SeccompUnwinder::PopSeccompStackFrame(RawContextCPU* cpu,
|
||||
const MDRawThread& thread,
|
||||
uint8_t* stack_copy) {
|
||||
#if defined(__x86_64)
|
||||
uint64_t bp = cpu->rbp;
|
||||
uint64_t top = thread.stack.start_of_memory_range;
|
||||
for (int i = 4; i--; ) {
|
||||
if (bp < top ||
|
||||
bp > thread.stack.start_of_memory_range +
|
||||
thread.stack.memory.data_size - sizeof(bp) ||
|
||||
bp & 1) {
|
||||
break;
|
||||
}
|
||||
uint64_t old_top = top;
|
||||
top = bp;
|
||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
||||
if (bp == 0xDEADBEEFDEADBEEFull) {
|
||||
struct {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rdi;
|
||||
uint64_t rsi;
|
||||
uint64_t rdx;
|
||||
uint64_t rcx;
|
||||
uint64_t rbx;
|
||||
uint64_t deadbeef;
|
||||
uint64_t rbp;
|
||||
uint64_t fakeret;
|
||||
uint64_t ret;
|
||||
/* char redzone[128]; */
|
||||
} seccomp_stackframe;
|
||||
if (top - offsetof(__typeof__(seccomp_stackframe), deadbeef) < old_top ||
|
||||
top - offsetof(__typeof__(seccomp_stackframe), deadbeef) +
|
||||
sizeof(seccomp_stackframe) >
|
||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||
break;
|
||||
}
|
||||
my_memcpy(&seccomp_stackframe,
|
||||
bp_addr - offsetof(__typeof__(seccomp_stackframe), deadbeef),
|
||||
sizeof(seccomp_stackframe));
|
||||
cpu->rbx = seccomp_stackframe.rbx;
|
||||
cpu->rcx = seccomp_stackframe.rcx;
|
||||
cpu->rdx = seccomp_stackframe.rdx;
|
||||
cpu->rsi = seccomp_stackframe.rsi;
|
||||
cpu->rdi = seccomp_stackframe.rdi;
|
||||
cpu->rbp = seccomp_stackframe.rbp;
|
||||
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
|
||||
cpu->r8 = seccomp_stackframe.r8;
|
||||
cpu->r9 = seccomp_stackframe.r9;
|
||||
cpu->r10 = seccomp_stackframe.r10;
|
||||
cpu->r11 = seccomp_stackframe.r11;
|
||||
cpu->r12 = seccomp_stackframe.r12;
|
||||
cpu->r13 = seccomp_stackframe.r13;
|
||||
cpu->r14 = seccomp_stackframe.r14;
|
||||
cpu->r15 = seccomp_stackframe.r15;
|
||||
cpu->rip = seccomp_stackframe.fakeret;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
uint32_t bp = cpu->ebp;
|
||||
uint32_t top = thread.stack.start_of_memory_range;
|
||||
for (int i = 4; i--; ) {
|
||||
if (bp < top ||
|
||||
bp > thread.stack.start_of_memory_range +
|
||||
thread.stack.memory.data_size - sizeof(bp) ||
|
||||
bp & 1) {
|
||||
break;
|
||||
}
|
||||
uint32_t old_top = top;
|
||||
top = bp;
|
||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
||||
if (bp == 0xDEADBEEFu) {
|
||||
struct {
|
||||
uint32_t edi;
|
||||
uint32_t esi;
|
||||
uint32_t edx;
|
||||
uint32_t ecx;
|
||||
uint32_t ebx;
|
||||
uint32_t deadbeef;
|
||||
uint32_t ebp;
|
||||
uint32_t fakeret;
|
||||
uint32_t ret;
|
||||
} seccomp_stackframe;
|
||||
if (top - offsetof(__typeof__(seccomp_stackframe), deadbeef) < old_top ||
|
||||
top - offsetof(__typeof__(seccomp_stackframe), deadbeef) +
|
||||
sizeof(seccomp_stackframe) >
|
||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||
break;
|
||||
}
|
||||
my_memcpy(&seccomp_stackframe,
|
||||
bp_addr - offsetof(__typeof__(seccomp_stackframe), deadbeef),
|
||||
sizeof(seccomp_stackframe));
|
||||
cpu->ebx = seccomp_stackframe.ebx;
|
||||
cpu->ecx = seccomp_stackframe.ecx;
|
||||
cpu->edx = seccomp_stackframe.edx;
|
||||
cpu->esi = seccomp_stackframe.esi;
|
||||
cpu->edi = seccomp_stackframe.edi;
|
||||
cpu->ebp = seccomp_stackframe.ebp;
|
||||
cpu->esp = top + 4*sizeof(void*);
|
||||
cpu->eip = seccomp_stackframe.fakeret;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
50
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/seccomp_unwinder.h
vendored
Normal file
50
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/seccomp_unwinder.h
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
// 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_SECCOMP_UNWINDER_H
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
|
||||
|
||||
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct SeccompUnwinder {
|
||||
|
||||
// Check if the top of the stack is part of a system call that has been
|
||||
// redirected by the seccomp sandbox. If so, try to pop the stack frames
|
||||
// all the way back to the point where the interception happened.
|
||||
static void PopSeccompStackFrame(RawContextCPU* cpu,
|
||||
const MDRawThread& thread,
|
||||
uint8_t* stack_copy);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
|
299
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/thread_info.cc
vendored
Normal file
299
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/thread_info.cc
vendored
Normal file
|
@ -0,0 +1,299 @@
|
|||
// 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 {
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
|
||||
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
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/thread_info.h
vendored
Normal file
91
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/thread_info.h
vendored
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 <asm/ptrace.h>
|
||||
struct user_pt_regs regs;
|
||||
struct user_fpsimd_state 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_
|
253
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc
vendored
Normal file
253
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc
vendored
Normal file
|
@ -0,0 +1,253 @@
|
|||
// 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) {
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
|
||||
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
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h
vendored
Normal file
64
TMessagesProj/jni/third_party/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h
vendored
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
|
743
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/exception_handler.cc
vendored
Normal file
743
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/exception_handler.cc
vendored
Normal file
|
@ -0,0 +1,743 @@
|
|||
// 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.
|
||||
|
||||
// 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
|
||||
//
|
||||
|
||||
// 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 <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
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
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
|
||||
};
|
||||
const int kNumHandledSignals =
|
||||
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
|
||||
struct sigaction old_handlers[kNumHandledSignals];
|
||||
bool handlers_installed = false;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const int server_fd)
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
minidump_descriptor_(descriptor),
|
||||
crash_handler_(NULL) {
|
||||
if (server_fd >= 0)
|
||||
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
|
||||
!minidump_descriptor_.IsMicrodumpOnConsole())
|
||||
minidump_descriptor_.UpdatePath();
|
||||
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
if (!g_handler_stack_)
|
||||
g_handler_stack_ = new std::vector<ExceptionHandler*>;
|
||||
if (install_handler) {
|
||||
InstallAlternateStackLocked();
|
||||
InstallHandlersLocked();
|
||||
}
|
||||
g_handler_stack_->push_back(this);
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::~ExceptionHandler() {
|
||||
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.
|
||||
}
|
||||
}
|
||||
handlers_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
// static
|
||||
void ExceptionHandler::RestoreHandlersLocked() {
|
||||
if (!handlers_installed)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
|
||||
InstallDefaultHandler(kExceptionSignals[i]);
|
||||
}
|
||||
}
|
||||
handlers_installed = false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
CrashContext context;
|
||||
// Fill in all the holes in the struct to make Valgrind happy.
|
||||
memset(&context, 0, sizeof(context));
|
||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&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(&context.float_state, fp_ptr, sizeof(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(&context.float_state,
|
||||
uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
}
|
||||
#endif
|
||||
context.tid = syscall(__NR_gettid);
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&context, sizeof(context), callback_context_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GenerateDump(&context);
|
||||
}
|
||||
|
||||
// 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_(minidump_descriptor_, callback_context_, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
// 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_build_fingerprint(),
|
||||
minidump_descriptor_.microdump_product_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
|
278
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/exception_handler.h
vendored
Normal file
278
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/exception_handler.h
vendored
Normal file
|
@ -0,0 +1,278 @@
|
|||
// 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_HANDLER_EXCEPTION_HANDLER_H_
|
||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#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 {
|
||||
|
||||
// ExceptionHandler
|
||||
//
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
// or when WriteMinidump() is called explicitly by your program.
|
||||
//
|
||||
// To have the exception handler write minidumps when an uncaught exception
|
||||
// (crash) occurs, you should create an instance early in the execution
|
||||
// of your program, and keep it around for the entire time you want to
|
||||
// have crash handling active (typically, until shutdown).
|
||||
// (NOTE): There should be only be one this kind of exception handler
|
||||
// object per process.
|
||||
//
|
||||
// If you want to write minidumps without installing the exception handler,
|
||||
// you can create an ExceptionHandler with install_handler set to false,
|
||||
// then call WriteMinidump. You can also use this technique if you want to
|
||||
// use different minidump callbacks for different call sites.
|
||||
//
|
||||
// In either case, a callback function is called when a minidump is written,
|
||||
// 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
|
||||
// callback_context when the handler was created.
|
||||
//
|
||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
||||
// attempting to write a minidump. If a FilterCallback returns false,
|
||||
// Breakpad will immediately report the exception as unhandled without
|
||||
// writing a minidump, allowing another handler the opportunity to handle it.
|
||||
typedef bool (*FilterCallback)(void *context);
|
||||
|
||||
// A callback function to run after the minidump has been 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
|
||||
// being notified of the exception. If the callback returns false, Breakpad
|
||||
// will treat the exception as unhandled, and allow another handler to handle
|
||||
// it. If there are no other handlers, Breakpad will report the exception to
|
||||
// the system as unhandled, allowing a debugger or native crash dialog the
|
||||
// opportunity to handle the exception. Most callback implementations
|
||||
// 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 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.
|
||||
// Its return value determines whether or not Breakpad should write a
|
||||
// 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.
|
||||
// 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();
|
||||
|
||||
const MinidumpDescriptor& minidump_descriptor() const {
|
||||
return minidump_descriptor_;
|
||||
}
|
||||
|
||||
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,
|
||||
MinidumpCallback callback,
|
||||
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:
|
||||
// Save the old signal handlers and install new ones.
|
||||
static bool InstallHandlersLocked();
|
||||
// Restore the old signal handlers.
|
||||
static void RestoreHandlersLocked();
|
||||
|
||||
void PreresolveSymbols();
|
||||
bool GenerateDump(CrashContext *context);
|
||||
void SendContinueSignalToChild();
|
||||
void WaitForContinueSignal();
|
||||
|
||||
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);
|
||||
|
||||
const FilterCallback filter_;
|
||||
const MinidumpCallback callback_;
|
||||
void* const callback_context_;
|
||||
|
||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||
|
||||
MinidumpDescriptor minidump_descriptor_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// 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];
|
||||
|
||||
// Callers can add extra info about mappings for cases where the
|
||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||
MappingList mapping_list_;
|
||||
|
||||
// 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_
|
1195
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/exception_handler_unittest.cc
vendored
Normal file
1195
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/exception_handler_unittest.cc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
100
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/minidump_descriptor.cc
vendored
Normal file
100
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/minidump_descriptor.cc
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
// 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_build_fingerprint_(descriptor.microdump_build_fingerprint_),
|
||||
microdump_product_info_(descriptor.microdump_product_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_build_fingerprint_ = descriptor.microdump_build_fingerprint_;
|
||||
microdump_product_info_ = descriptor.microdump_product_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();
|
||||
}
|
||||
|
||||
void MinidumpDescriptor::SetMicrodumpBuildFingerprint(
|
||||
const char* build_fingerprint) {
|
||||
assert(mode_ == kWriteMicrodumpToConsole);
|
||||
microdump_build_fingerprint_ = build_fingerprint;
|
||||
}
|
||||
|
||||
void MinidumpDescriptor::SetMicrodumpProductInfo(const char* product_info) {
|
||||
assert(mode_ == kWriteMicrodumpToConsole);
|
||||
microdump_product_info_ = product_info;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
161
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/minidump_descriptor.h
vendored
Normal file
161
TMessagesProj/jni/third_party/breakpad/src/client/linux/handler/minidump_descriptor.h
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
// 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 "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),
|
||||
microdump_build_fingerprint_(NULL),
|
||||
microdump_product_info_(NULL) {}
|
||||
|
||||
explicit MinidumpDescriptor(const string& directory)
|
||||
: mode_(kWriteMinidumpToFile),
|
||||
fd_(-1),
|
||||
directory_(directory),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1),
|
||||
microdump_build_fingerprint_(NULL),
|
||||
microdump_product_info_(NULL) {
|
||||
assert(!directory.empty());
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(int fd)
|
||||
: mode_(kWriteMinidumpToFd),
|
||||
fd_(fd),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1),
|
||||
microdump_build_fingerprint_(NULL),
|
||||
microdump_product_info_(NULL) {
|
||||
assert(fd != -1);
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
|
||||
: mode_(kWriteMicrodumpToConsole),
|
||||
fd_(-1),
|
||||
size_limit_(-1),
|
||||
microdump_build_fingerprint_(NULL),
|
||||
microdump_product_info_(NULL) {}
|
||||
|
||||
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; }
|
||||
|
||||
// TODO(primiano): make this and product info (below) just part of the
|
||||
// microdump ctor once it is rolled stably into Chrome. ETA: June 2015.
|
||||
void SetMicrodumpBuildFingerprint(const char* build_fingerprint);
|
||||
const char* microdump_build_fingerprint() const {
|
||||
return microdump_build_fingerprint_;
|
||||
}
|
||||
|
||||
void SetMicrodumpProductInfo(const char* product_info);
|
||||
const char* microdump_product_info() const {
|
||||
return microdump_product_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 product name/version and build 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 product details must be provided upfront.
|
||||
// The string pointers are supposed to be valid through all the lifetime of
|
||||
// the process (read: the caller has to guarantee that they are stored in
|
||||
// global static storage).
|
||||
const char* microdump_build_fingerprint_;
|
||||
const char* microdump_product_info_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
|
@ -0,0 +1,48 @@
|
|||
// 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>
|
||||
#else
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#endif
|
||||
|
||||
namespace logger {
|
||||
|
||||
int write(const char* buf, size_t nbytes) {
|
||||
#if defined(__ANDROID__)
|
||||
return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf);
|
||||
#else
|
||||
return sys_write(2, buf, nbytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace logger
|
|
@ -0,0 +1,41 @@
|
|||
// 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);
|
||||
|
||||
} // namespace logger
|
||||
|
||||
#endif // CLIENT_LINUX_LOG_LOG_H_
|
423
TMessagesProj/jni/third_party/breakpad/src/client/linux/microdump_writer/microdump_writer.cc
vendored
Normal file
423
TMessagesProj/jni/third_party/breakpad/src/client/linux/microdump_writer/microdump_writer.cc
vendored
Normal file
|
@ -0,0 +1,423 @@
|
|||
// 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 <sys/utsname.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/seccomp_unwinder.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/log/log.h"
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using google_breakpad::LinuxDumper;
|
||||
using google_breakpad::LinuxPtraceDumper;
|
||||
using google_breakpad::MappingInfo;
|
||||
using google_breakpad::MappingList;
|
||||
using google_breakpad::RawContextCPU;
|
||||
using google_breakpad::SeccompUnwinder;
|
||||
using google_breakpad::ThreadInfo;
|
||||
using google_breakpad::UContextReader;
|
||||
|
||||
const size_t kLineBufferSize = 2048;
|
||||
|
||||
class MicrodumpWriter {
|
||||
public:
|
||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
const char* build_fingerprint,
|
||||
const char* product_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),
|
||||
build_fingerprint_(build_fingerprint),
|
||||
product_info_(product_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();
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
bool success;
|
||||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||
DumpProductInformation();
|
||||
DumpOSInformation();
|
||||
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) {
|
||||
logger::write(msg, my_strlen(msg));
|
||||
#if !defined(__ANDROID__)
|
||||
logger::write("\n", 1); // Android logger appends the \n. Linux's doesn't.
|
||||
#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 (product_info_) {
|
||||
LogAppend(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
|
||||
|
||||
// We cannot depend on uts.machine. On multiarch devices it always returns the
|
||||
// primary arch, not the one that match the executable being run.
|
||||
#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__)
|
||||
const char kArch[] = "mips";
|
||||
#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(" ");
|
||||
// If the client has attached a build fingerprint to the MinidumpDescriptor
|
||||
// use that one. Otherwise try to get some basic info from uname().
|
||||
if (build_fingerprint_) {
|
||||
LogAppend(build_fingerprint_);
|
||||
} else {
|
||||
struct utsname uts;
|
||||
if (uname(&uts) == 0) {
|
||||
LogAppend(uts.machine);
|
||||
LogAppend(" ");
|
||||
LogAppend(uts.release);
|
||||
LogAppend(" ");
|
||||
LogAppend(uts.version);
|
||||
} else {
|
||||
LogAppend("no build fingerprint available");
|
||||
}
|
||||
}
|
||||
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
|
||||
if (stack_copy)
|
||||
SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy);
|
||||
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) {
|
||||
MDGUID module_identifier;
|
||||
if (identifier) {
|
||||
// GUID was provided by caller.
|
||||
my_memcpy(&module_identifier, identifier, sizeof(MDGUID));
|
||||
} else {
|
||||
dumper_->ElfFileIdentifierForMapping(
|
||||
mapping,
|
||||
member,
|
||||
mapping_id,
|
||||
reinterpret_cast<uint8_t*>(&module_identifier));
|
||||
}
|
||||
|
||||
char file_name[NAME_MAX];
|
||||
char file_path[NAME_MAX];
|
||||
LinuxDumper::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();
|
||||
}
|
||||
|
||||
// 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 char* const build_fingerprint_;
|
||||
const char* const product_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 char* build_fingerprint,
|
||||
const char* product_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, build_fingerprint, product_info,
|
||||
&dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
64
TMessagesProj/jni/third_party/breakpad/src/client/linux/microdump_writer/microdump_writer.h
vendored
Normal file
64
TMessagesProj/jni/third_party/breakpad/src/client/linux/microdump_writer/microdump_writer.h
vendored
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_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 {
|
||||
|
||||
// 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 char* build_fingerprint,
|
||||
const char* product_info);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
|
@ -0,0 +1,164 @@
|
|||
// 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 <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test MicrodumpWriterTest;
|
||||
|
||||
void CrashAndGetMicrodump(
|
||||
const MappingList& mappings,
|
||||
const char* build_fingerprint,
|
||||
const char* product_info,
|
||||
scoped_array<char>* buf) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string stderr_file = temp_dir.path() + "/stderr.log";
|
||||
int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
ASSERT_NE(-1, err_fd);
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
|
||||
// Set a non-zero tid to avoid tripping asserts.
|
||||
context.tid = child;
|
||||
|
||||
// Redirect temporarily stderr to the stderr.log file.
|
||||
int save_err = dup(STDERR_FILENO);
|
||||
ASSERT_NE(-1, save_err);
|
||||
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
|
||||
|
||||
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
|
||||
build_fingerprint, product_info));
|
||||
|
||||
// Revert stderr back to the console.
|
||||
dup2(save_err, STDERR_FILENO);
|
||||
close(save_err);
|
||||
|
||||
// Read back the stderr file and check for the microdump marker.
|
||||
fsync(err_fd);
|
||||
lseek(err_fd, 0, SEEK_SET);
|
||||
const size_t kBufSize = 64 * 1024;
|
||||
buf->reset(new char[kBufSize]);
|
||||
ASSERT_GT(read(err_fd, buf->get(), kBufSize), 0);
|
||||
|
||||
close(err_fd);
|
||||
close(fds[1]);
|
||||
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----BEGIN BREAKPAD MICRODUMP-----"));
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----END BREAKPAD MICRODUMP-----"));
|
||||
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, BasicWithMappings) {
|
||||
// Push some extra mapping to check the MappingList logic.
|
||||
const uint32_t memory_size = sysconf(_SC_PAGESIZE);
|
||||
const char* kMemoryName = "libfoo.so";
|
||||
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
|
||||
MappingInfo info;
|
||||
info.start_addr = memory_size;
|
||||
info.size = memory_size;
|
||||
info.offset = 42;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
MappingEntry mapping;
|
||||
mapping.first = info;
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
|
||||
scoped_array<char> buf;
|
||||
CrashAndGetMicrodump(mappings, NULL, NULL, &buf);
|
||||
|
||||
#ifdef __LP64__
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#else
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 00001000 0000002A 00001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#endif
|
||||
|
||||
// In absence of a product info in the minidump, the writer should just write
|
||||
// an unknown marker.
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "V UNKNOWN:0.0.0.0"));
|
||||
}
|
||||
|
||||
// Ensure that the product info and build fingerprint metadata show up in the
|
||||
// final microdump if present.
|
||||
TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
|
||||
const char kProductInfo[] = "MockProduct:42.0.2311.99";
|
||||
const char kBuildFingerprint[] =
|
||||
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
|
||||
scoped_array<char> buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kBuildFingerprint, kProductInfo, &buf);
|
||||
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(buf.get(), kBuildFingerprint));
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(buf.get(), kProductInfo));
|
||||
}
|
||||
|
||||
} // namespace
|
144
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/cpu_set.h
vendored
Normal file
144
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/cpu_set.h
vendored
Normal file
|
@ -0,0 +1,144 @@
|
|||
// Copyright (c) 2013, 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_CPU_SET_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Helper class used to model a set of CPUs, as read from sysfs
|
||||
// files like /sys/devices/system/cpu/present
|
||||
// See See http://www.kernel.org/doc/Documentation/cputopology.txt
|
||||
class CpuSet {
|
||||
public:
|
||||
// The maximum number of supported CPUs.
|
||||
static const size_t kMaxCpus = 1024;
|
||||
|
||||
CpuSet() {
|
||||
my_memset(mask_, 0, sizeof(mask_));
|
||||
}
|
||||
|
||||
// Parse a sysfs file to extract the corresponding CPU set.
|
||||
bool ParseSysFile(int fd) {
|
||||
char buffer[512];
|
||||
int ret = sys_read(fd, buffer, sizeof(buffer)-1);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
buffer[ret] = '\0';
|
||||
|
||||
// Expected format: comma-separated list of items, where each
|
||||
// item can be a decimal integer, or two decimal integers separated
|
||||
// by a dash.
|
||||
// E.g.:
|
||||
// 0
|
||||
// 0,1,2,3
|
||||
// 0-3
|
||||
// 1,10-23
|
||||
const char* p = buffer;
|
||||
const char* p_end = p + ret;
|
||||
while (p < p_end) {
|
||||
// Skip leading space, if any
|
||||
while (p < p_end && my_isspace(*p))
|
||||
p++;
|
||||
|
||||
// Find start and size of current item.
|
||||
const char* item = p;
|
||||
size_t item_len = static_cast<size_t>(p_end - p);
|
||||
const char* item_next =
|
||||
static_cast<const char*>(my_memchr(p, ',', item_len));
|
||||
if (item_next != NULL) {
|
||||
p = item_next + 1;
|
||||
item_len = static_cast<size_t>(item_next - item);
|
||||
} else {
|
||||
p = p_end;
|
||||
item_next = p_end;
|
||||
}
|
||||
|
||||
// Ignore trailing spaces.
|
||||
while (item_next > item && my_isspace(item_next[-1]))
|
||||
item_next--;
|
||||
|
||||
// skip empty items.
|
||||
if (item_next == item)
|
||||
continue;
|
||||
|
||||
// read first decimal value.
|
||||
uintptr_t start = 0;
|
||||
const char* next = my_read_decimal_ptr(&start, item);
|
||||
uintptr_t end = start;
|
||||
if (*next == '-')
|
||||
my_read_decimal_ptr(&end, next+1);
|
||||
|
||||
while (start <= end)
|
||||
SetBit(start++);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Intersect this CPU set with another one.
|
||||
void IntersectWith(const CpuSet& other) {
|
||||
for (size_t nn = 0; nn < kMaskWordCount; ++nn)
|
||||
mask_[nn] &= other.mask_[nn];
|
||||
}
|
||||
|
||||
// Return the number of CPUs in this set.
|
||||
int GetCount() {
|
||||
int result = 0;
|
||||
for (size_t nn = 0; nn < kMaskWordCount; ++nn) {
|
||||
result += __builtin_popcount(mask_[nn]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetBit(uintptr_t index) {
|
||||
size_t nn = static_cast<size_t>(index);
|
||||
if (nn < kMaxCpus)
|
||||
mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits));
|
||||
}
|
||||
|
||||
typedef uint32_t MaskWordType;
|
||||
static const size_t kMaskWordBits = 8*sizeof(MaskWordType);
|
||||
static const size_t kMaskWordCount =
|
||||
(kMaxCpus + kMaskWordBits - 1) / kMaskWordBits;
|
||||
|
||||
MaskWordType mask_[kMaskWordCount];
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
|
164
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc
vendored
Normal file
164
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
// Copyright (c) 2013, 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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/minidump_writer/cpu_set.h"
|
||||
#include "common/linux/tests/auto_testfile.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test CpuSetTest;
|
||||
|
||||
// Helper class to write test text file to a temporary file and return
|
||||
// its file descriptor.
|
||||
class ScopedTestFile : public AutoTestFile {
|
||||
public:
|
||||
explicit ScopedTestFile(const char* text)
|
||||
: AutoTestFile("cpu_set", text) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, EmptyCount) {
|
||||
CpuSet set;
|
||||
ASSERT_EQ(0, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, OneCpu) {
|
||||
ScopedTestFile file("10");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(1, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, OneCpuTerminated) {
|
||||
ScopedTestFile file("10\n");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(1, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, TwoCpusWithComma) {
|
||||
ScopedTestFile file("1,10");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(2, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, TwoCpusWithRange) {
|
||||
ScopedTestFile file("1-2");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(2, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, TenCpusWithRange) {
|
||||
ScopedTestFile file("9-18");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(10, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, MultiItems) {
|
||||
ScopedTestFile file("0, 2-4, 128");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(5, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, IntersectWith) {
|
||||
ScopedTestFile file1("9-19");
|
||||
ASSERT_TRUE(file1.IsOk());
|
||||
CpuSet set1;
|
||||
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
|
||||
ASSERT_EQ(11, set1.GetCount());
|
||||
|
||||
ScopedTestFile file2("16-24");
|
||||
ASSERT_TRUE(file2.IsOk());
|
||||
CpuSet set2;
|
||||
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
|
||||
ASSERT_EQ(9, set2.GetCount());
|
||||
|
||||
set1.IntersectWith(set2);
|
||||
ASSERT_EQ(4, set1.GetCount());
|
||||
ASSERT_EQ(9, set2.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, SelfIntersection) {
|
||||
ScopedTestFile file1("9-19");
|
||||
ASSERT_TRUE(file1.IsOk());
|
||||
CpuSet set1;
|
||||
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
|
||||
ASSERT_EQ(11, set1.GetCount());
|
||||
|
||||
set1.IntersectWith(set1);
|
||||
ASSERT_EQ(11, set1.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, EmptyIntersection) {
|
||||
ScopedTestFile file1("0-19");
|
||||
ASSERT_TRUE(file1.IsOk());
|
||||
CpuSet set1;
|
||||
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
|
||||
ASSERT_EQ(20, set1.GetCount());
|
||||
|
||||
ScopedTestFile file2("20-39");
|
||||
ASSERT_TRUE(file2.IsOk());
|
||||
CpuSet set2;
|
||||
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
|
||||
ASSERT_EQ(20, set2.GetCount());
|
||||
|
||||
set1.IntersectWith(set2);
|
||||
ASSERT_EQ(0, set1.GetCount());
|
||||
|
||||
ASSERT_EQ(20, set2.GetCount());
|
||||
}
|
||||
|
106
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/directory_reader.h
vendored
Normal file
106
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/directory_reader.h
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2009, 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_DIRECTORY_READER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// A class for enumerating a directory without using diropen/readdir or other
|
||||
// functions which may allocate memory.
|
||||
class DirectoryReader {
|
||||
public:
|
||||
DirectoryReader(int fd)
|
||||
: fd_(fd),
|
||||
buf_used_(0) {
|
||||
}
|
||||
|
||||
// Return the next entry from the directory
|
||||
// name: (output) the NUL terminated entry name
|
||||
//
|
||||
// Returns true iff successful (false on EOF).
|
||||
//
|
||||
// After calling this, one must call |PopEntry| otherwise you'll get the same
|
||||
// entry over and over.
|
||||
bool GetNextEntry(const char** name) {
|
||||
struct kernel_dirent* const dent =
|
||||
reinterpret_cast<kernel_dirent*>(buf_);
|
||||
|
||||
if (buf_used_ == 0) {
|
||||
// need to read more entries.
|
||||
const int n = sys_getdents(fd_, dent, sizeof(buf_));
|
||||
if (n < 0) {
|
||||
return false;
|
||||
} else if (n == 0) {
|
||||
hit_eof_ = true;
|
||||
} else {
|
||||
buf_used_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf_used_ == 0 && hit_eof_)
|
||||
return false;
|
||||
|
||||
assert(buf_used_ > 0);
|
||||
|
||||
*name = dent->d_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PopEntry() {
|
||||
if (!buf_used_)
|
||||
return;
|
||||
|
||||
const struct kernel_dirent* const dent =
|
||||
reinterpret_cast<kernel_dirent*>(buf_);
|
||||
|
||||
buf_used_ -= dent->d_reclen;
|
||||
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
||||
}
|
||||
|
||||
private:
|
||||
const int fd_;
|
||||
bool hit_eof_;
|
||||
unsigned buf_used_;
|
||||
uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1];
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) 2009, 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 <set>
|
||||
#include <string>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "client/linux/minidump_writer/directory_reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
typedef testing::Test DirectoryReaderTest;
|
||||
}
|
||||
|
||||
TEST(DirectoryReaderTest, CompareResults) {
|
||||
std::set<string> dent_set;
|
||||
|
||||
DIR *const dir = opendir("/proc/self");
|
||||
ASSERT_TRUE(dir != NULL);
|
||||
|
||||
struct dirent* dent;
|
||||
while ((dent = readdir(dir)))
|
||||
dent_set.insert(dent->d_name);
|
||||
|
||||
closedir(dir);
|
||||
|
||||
const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY);
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
DirectoryReader dir_reader(fd);
|
||||
unsigned seen = 0;
|
||||
|
||||
const char* name;
|
||||
while (dir_reader.GetNextEntry(&name)) {
|
||||
ASSERT_TRUE(dent_set.find(name) != dent_set.end());
|
||||
seen++;
|
||||
dir_reader.PopEntry();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(dent_set.find("status") != dent_set.end());
|
||||
ASSERT_TRUE(dent_set.find("stat") != dent_set.end());
|
||||
ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end());
|
||||
|
||||
ASSERT_EQ(dent_set.size(), seen);
|
||||
close(fd);
|
||||
}
|
131
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/line_reader.h
vendored
Normal file
131
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/line_reader.h
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Copyright (c) 2009, 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_LINE_READER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// A class for reading a file, line by line, without using fopen/fgets or other
|
||||
// functions which may allocate memory.
|
||||
class LineReader {
|
||||
public:
|
||||
LineReader(int fd)
|
||||
: fd_(fd),
|
||||
hit_eof_(false),
|
||||
buf_used_(0) {
|
||||
}
|
||||
|
||||
// The maximum length of a line.
|
||||
static const size_t kMaxLineLen = 512;
|
||||
|
||||
// Return the next line from the file.
|
||||
// line: (output) a pointer to the start of the line. The line is NUL
|
||||
// terminated.
|
||||
// len: (output) the length of the line (not inc the NUL byte)
|
||||
//
|
||||
// Returns true iff successful (false on EOF).
|
||||
//
|
||||
// One must call |PopLine| after this function, otherwise you'll continue to
|
||||
// get the same line over and over.
|
||||
bool GetNextLine(const char **line, unsigned *len) {
|
||||
for (;;) {
|
||||
if (buf_used_ == 0 && hit_eof_)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < buf_used_; ++i) {
|
||||
if (buf_[i] == '\n' || buf_[i] == 0) {
|
||||
buf_[i] = 0;
|
||||
*len = i;
|
||||
*line = buf_;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf_used_ == sizeof(buf_)) {
|
||||
// we scanned the whole buffer and didn't find an end-of-line marker.
|
||||
// This line is too long to process.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We didn't find any end-of-line terminators in the buffer. However, if
|
||||
// this is the last line in the file it might not have one:
|
||||
if (hit_eof_) {
|
||||
assert(buf_used_);
|
||||
// There's room for the NUL because of the buf_used_ == sizeof(buf_)
|
||||
// check above.
|
||||
buf_[buf_used_] = 0;
|
||||
*len = buf_used_;
|
||||
buf_used_ += 1; // since we appended the NUL.
|
||||
*line = buf_;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we should pull in more data from the file
|
||||
const ssize_t n = sys_read(fd_, buf_ + buf_used_,
|
||||
sizeof(buf_) - buf_used_);
|
||||
if (n < 0) {
|
||||
return false;
|
||||
} else if (n == 0) {
|
||||
hit_eof_ = true;
|
||||
} else {
|
||||
buf_used_ += n;
|
||||
}
|
||||
|
||||
// At this point, we have either set the hit_eof_ flag, or we have more
|
||||
// data to process...
|
||||
}
|
||||
}
|
||||
|
||||
void PopLine(unsigned len) {
|
||||
// len doesn't include the NUL byte at the end.
|
||||
|
||||
assert(buf_used_ >= len + 1);
|
||||
buf_used_ -= len + 1;
|
||||
my_memmove(buf_, buf_ + len + 1, buf_used_);
|
||||
}
|
||||
|
||||
private:
|
||||
const int fd_;
|
||||
|
||||
bool hit_eof_;
|
||||
unsigned buf_used_;
|
||||
char buf_[kMaxLineLen];
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
|
169
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc
vendored
Normal file
169
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
// Copyright (c) 2009, 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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/tests/auto_testfile.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test LineReaderTest;
|
||||
|
||||
class ScopedTestFile : public AutoTestFile {
|
||||
public:
|
||||
explicit ScopedTestFile(const char* text)
|
||||
: AutoTestFile("line_reader", text) {
|
||||
}
|
||||
|
||||
ScopedTestFile(const char* text, size_t text_len)
|
||||
: AutoTestFile("line_reader", text, text_len) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, EmptyFile) {
|
||||
ScopedTestFile file("");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, OneLineTerminated) {
|
||||
ScopedTestFile file("a\n");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
|
||||
const char *line;
|
||||
unsigned int len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned int)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, OneLine) {
|
||||
ScopedTestFile file("a");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, TwoLinesTerminated) {
|
||||
ScopedTestFile file("a\nb\n");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('b', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, TwoLines) {
|
||||
ScopedTestFile file("a\nb");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('b', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, MaxLength) {
|
||||
char l[LineReader::kMaxLineLen-1];
|
||||
memset(l, 'a', sizeof(l));
|
||||
ScopedTestFile file(l, sizeof(l));
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(sizeof(l), len);
|
||||
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
|
||||
ASSERT_EQ('\0', line[len]);
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, TooLong) {
|
||||
// Note: this writes kMaxLineLen 'a' chars in the test file.
|
||||
char l[LineReader::kMaxLineLen];
|
||||
memset(l, 'a', sizeof(l));
|
||||
ScopedTestFile file(l, sizeof(l));
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
}
|
257
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc
vendored
Normal file
257
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc
vendored
Normal file
|
@ -0,0 +1,257 @@
|
|||
// 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.
|
||||
|
||||
// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
|
||||
// See linux_core_dumper.h for details.
|
||||
|
||||
#include "client/linux/minidump_writer/linux_core_dumper.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/procfs.h>
|
||||
#if defined(__mips__) && defined(__ANDROID__)
|
||||
// To get register definitions.
|
||||
#include <asm/reg.h>
|
||||
#endif
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
|
||||
const char* core_path,
|
||||
const char* procfs_path)
|
||||
: LinuxDumper(pid),
|
||||
core_path_(core_path),
|
||||
procfs_path_(procfs_path),
|
||||
thread_infos_(&allocator_, 8) {
|
||||
assert(core_path_);
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
|
||||
const char* node) const {
|
||||
if (!path || !node)
|
||||
return false;
|
||||
|
||||
size_t node_len = my_strlen(node);
|
||||
if (node_len == 0)
|
||||
return false;
|
||||
|
||||
size_t procfs_path_len = my_strlen(procfs_path_);
|
||||
size_t total_length = procfs_path_len + 1 + node_len;
|
||||
if (total_length >= NAME_MAX)
|
||||
return false;
|
||||
|
||||
memcpy(path, procfs_path_, procfs_path_len);
|
||||
path[procfs_path_len] = '/';
|
||||
memcpy(path + procfs_path_len + 1, node, node_len);
|
||||
path[total_length] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
const void* src, size_t length) {
|
||||
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
|
||||
// TODO(benchan): Investigate whether the data to be copied could span
|
||||
// across multiple segments in the core dump file. ElfCoreDump::CopyData
|
||||
// and this method do not handle that case yet.
|
||||
if (!core_.CopyData(dest, virtual_address, length)) {
|
||||
// If the data segment is not found in the core dump, fill the result
|
||||
// with marker characters.
|
||||
memset(dest, 0xab, length);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
if (index >= thread_infos_.size())
|
||||
return false;
|
||||
|
||||
*info = thread_infos_[index];
|
||||
const uint8_t* stack_pointer;
|
||||
#if defined(__i386)
|
||||
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||
#elif defined(__x86_64)
|
||||
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||
#elif defined(__ARM_EABI__)
|
||||
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||
#elif defined(__aarch64__)
|
||||
memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
||||
#elif defined(__mips__)
|
||||
stack_pointer =
|
||||
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::IsPostMortem() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::ThreadsSuspend() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::ThreadsResume() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::EnumerateThreads() {
|
||||
if (!mapped_core_file_.Map(core_path_, 0)) {
|
||||
fprintf(stderr, "Could not map core dump file into memory\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
core_.SetContent(mapped_core_file_.content());
|
||||
if (!core_.IsValid()) {
|
||||
fprintf(stderr, "Invalid core dump file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfCoreDump::Note note = core_.GetFirstNote();
|
||||
if (!note.IsValid()) {
|
||||
fprintf(stderr, "PT_NOTE section not found\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool first_thread = true;
|
||||
do {
|
||||
ElfCoreDump::Word type = note.GetType();
|
||||
MemoryRange name = note.GetName();
|
||||
MemoryRange description = note.GetDescription();
|
||||
|
||||
if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
|
||||
fprintf(stderr, "Could not found a valid PT_NOTE.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
|
||||
// ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
|
||||
// Thread Name Type
|
||||
// -------------------------------------------------------------------
|
||||
// 1st thread CORE NT_PRSTATUS
|
||||
// process-wide CORE NT_PRPSINFO
|
||||
// process-wide CORE NT_AUXV
|
||||
// 1st thread CORE NT_FPREGSET
|
||||
// 1st thread LINUX NT_PRXFPREG
|
||||
// 1st thread LINUX NT_386_TLS
|
||||
//
|
||||
// 2nd thread CORE NT_PRSTATUS
|
||||
// 2nd thread CORE NT_FPREGSET
|
||||
// 2nd thread LINUX NT_PRXFPREG
|
||||
// 2nd thread LINUX NT_386_TLS
|
||||
//
|
||||
// 3rd thread CORE NT_PRSTATUS
|
||||
// 3rd thread CORE NT_FPREGSET
|
||||
// 3rd thread LINUX NT_PRXFPREG
|
||||
// 3rd thread LINUX NT_386_TLS
|
||||
//
|
||||
// The following code only works if notes are ordered as expected.
|
||||
switch (type) {
|
||||
case NT_PRSTATUS: {
|
||||
if (description.length() != sizeof(elf_prstatus)) {
|
||||
fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const elf_prstatus* status =
|
||||
reinterpret_cast<const elf_prstatus*>(description.data());
|
||||
pid_t pid = status->pr_pid;
|
||||
ThreadInfo info;
|
||||
memset(&info, 0, sizeof(ThreadInfo));
|
||||
info.tgid = status->pr_pgrp;
|
||||
info.ppid = status->pr_ppid;
|
||||
#if defined(__mips__)
|
||||
#if defined(__ANDROID__)
|
||||
for (int i = EF_R0; i <= EF_R31; i++)
|
||||
info.mcontext.gregs[i - EF_R0] = status->pr_reg[i];
|
||||
#else // __ANDROID__
|
||||
for (int i = EF_REG0; i <= EF_REG31; i++)
|
||||
info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i];
|
||||
#endif // __ANDROID__
|
||||
info.mcontext.mdlo = status->pr_reg[EF_LO];
|
||||
info.mcontext.mdhi = status->pr_reg[EF_HI];
|
||||
info.mcontext.pc = status->pr_reg[EF_CP0_EPC];
|
||||
#else // __mips__
|
||||
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
|
||||
#endif // __mips__
|
||||
if (first_thread) {
|
||||
crash_thread_ = pid;
|
||||
crash_signal_ = status->pr_info.si_signo;
|
||||
}
|
||||
first_thread = false;
|
||||
threads_.push_back(pid);
|
||||
thread_infos_.push_back(info);
|
||||
break;
|
||||
}
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
case NT_FPREGSET: {
|
||||
if (thread_infos_.empty())
|
||||
return false;
|
||||
|
||||
ThreadInfo* info = &thread_infos_.back();
|
||||
if (description.length() != sizeof(info->fpregs)) {
|
||||
fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if defined(__i386)
|
||||
case NT_PRXFPREG: {
|
||||
if (thread_infos_.empty())
|
||||
return false;
|
||||
|
||||
ThreadInfo* info = &thread_infos_.back();
|
||||
if (description.length() != sizeof(info->fpxregs)) {
|
||||
fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
note = note.GetNextNote();
|
||||
} while (note.IsValid());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
123
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_core_dumper.h
vendored
Normal file
123
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_core_dumper.h
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
// 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.
|
||||
|
||||
// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
|
||||
// class, which is derived from google_breakpad::LinuxDumper to extract
|
||||
// information from a crashed process via its core dump and proc files.
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "common/linux/elf_core_dump.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class LinuxCoreDumper : public LinuxDumper {
|
||||
public:
|
||||
// Constructs a dumper for extracting information of a given process
|
||||
// with a process ID of |pid| via its core dump file at |core_path| and
|
||||
// its proc files at |procfs_path|. If |procfs_path| is a copy of
|
||||
// /proc/<pid>, it should contain the following files:
|
||||
// auxv, cmdline, environ, exe, maps, status
|
||||
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path);
|
||||
|
||||
// Implements LinuxDumper::BuildProcPath().
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
// |path| is a character array of at least NAME_MAX bytes to return the
|
||||
// result.|node| is the final node without any slashes. Return true on
|
||||
// success.
|
||||
//
|
||||
// As this dumper performs a post-mortem dump and makes use of a copy
|
||||
// of the proc files of the crashed process, this derived method does
|
||||
// not actually make use of |pid| and always returns a subpath of
|
||||
// |procfs_path_| regardless of whether |pid| corresponds to the main
|
||||
// process or a thread of the process, i.e. assuming both the main process
|
||||
// and its threads have the following proc files with the same content:
|
||||
// auxv, cmdline, environ, exe, maps, status
|
||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
|
||||
|
||||
// Implements LinuxDumper::CopyFromProcess().
|
||||
// Copies content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|. This method extracts the content
|
||||
// the core dump and fills |dest| with a sequence of marker bytes
|
||||
// if the expected data is not found in the core dump. Returns true if
|
||||
// the expected data is found in the core dump.
|
||||
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length);
|
||||
|
||||
// Implements LinuxDumper::GetThreadInfoByIndex().
|
||||
// Reads information about the |index|-th thread of |threads_|.
|
||||
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
|
||||
|
||||
// Implements LinuxDumper::IsPostMortem().
|
||||
// Always returns true to indicate that this dumper performs a
|
||||
// post-mortem dump of a crashed process via a core dump file.
|
||||
virtual bool IsPostMortem() const;
|
||||
|
||||
// Implements LinuxDumper::ThreadsSuspend().
|
||||
// As the dumper performs a post-mortem dump via a core dump file,
|
||||
// there is no threads to suspend. This method does nothing and
|
||||
// always returns true.
|
||||
virtual bool ThreadsSuspend();
|
||||
|
||||
// Implements LinuxDumper::ThreadsResume().
|
||||
// As the dumper performs a post-mortem dump via a core dump file,
|
||||
// there is no threads to resume. This method does nothing and
|
||||
// always returns true.
|
||||
virtual bool ThreadsResume();
|
||||
|
||||
protected:
|
||||
// Implements LinuxDumper::EnumerateThreads().
|
||||
// Enumerates all threads of the given process into |threads_|.
|
||||
virtual bool EnumerateThreads();
|
||||
|
||||
private:
|
||||
// Path of the core dump file.
|
||||
const char* core_path_;
|
||||
|
||||
// Path of the directory containing the proc files of the given process,
|
||||
// which is usually a copy of /proc/<pid>.
|
||||
const char* procfs_path_;
|
||||
|
||||
// Memory-mapped core dump file at |core_path_|.
|
||||
MemoryMappedFile mapped_core_file_;
|
||||
|
||||
// Content of the core dump file.
|
||||
ElfCoreDump core_;
|
||||
|
||||
// Thread info found in the core dump file.
|
||||
wasteful_vector<ThreadInfo> thread_infos_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_
|
|
@ -0,0 +1,118 @@
|
|||
// 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.
|
||||
|
||||
// linux_core_dumper_unittest.cc:
|
||||
// Unit tests for google_breakpad::LinuxCoreDumoer.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/minidump_writer/linux_core_dumper.h"
|
||||
#include "common/linux/tests/crash_generator.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
TEST(LinuxCoreDumperTest, BuildProcPath) {
|
||||
const pid_t pid = getpid();
|
||||
const char procfs_path[] = "/procfs_copy";
|
||||
LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
|
||||
|
||||
char maps_path[NAME_MAX] = "";
|
||||
char maps_path_expected[NAME_MAX];
|
||||
snprintf(maps_path_expected, sizeof(maps_path_expected),
|
||||
"%s/maps", procfs_path);
|
||||
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
|
||||
EXPECT_STREQ(maps_path_expected, maps_path);
|
||||
|
||||
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
|
||||
|
||||
char long_node[NAME_MAX];
|
||||
size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
|
||||
memset(long_node, 'a', long_node_len);
|
||||
long_node[long_node_len] = '\0';
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
|
||||
}
|
||||
|
||||
TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
|
||||
CrashGenerator crash_generator;
|
||||
if (!crash_generator.HasDefaultCorePattern()) {
|
||||
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
|
||||
"is skipped due to non-default core pattern\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned kNumOfThreads = 3;
|
||||
const unsigned kCrashThread = 1;
|
||||
const int kCrashSignal = SIGABRT;
|
||||
pid_t child_pid;
|
||||
ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
|
||||
kCrashSignal, &child_pid));
|
||||
|
||||
const string core_file = crash_generator.GetCoreFilePath();
|
||||
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
struct stat st;
|
||||
if (stat(core_file.c_str(), &st) != 0) {
|
||||
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
|
||||
"skipped due to no core file being generated");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
|
||||
|
||||
EXPECT_TRUE(dumper.Init());
|
||||
|
||||
EXPECT_TRUE(dumper.IsPostMortem());
|
||||
|
||||
// These are no-ops and should always return true.
|
||||
EXPECT_TRUE(dumper.ThreadsSuspend());
|
||||
EXPECT_TRUE(dumper.ThreadsResume());
|
||||
|
||||
// LinuxCoreDumper cannot determine the crash address and thus it always
|
||||
// sets the crash address to 0.
|
||||
EXPECT_EQ(0U, dumper.crash_address());
|
||||
EXPECT_EQ(kCrashSignal, dumper.crash_signal());
|
||||
EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
|
||||
dumper.crash_thread());
|
||||
|
||||
EXPECT_EQ(kNumOfThreads, dumper.threads().size());
|
||||
for (unsigned i = 0; i < kNumOfThreads; ++i) {
|
||||
ThreadInfo info;
|
||||
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer));
|
||||
EXPECT_EQ(getpid(), info.ppid);
|
||||
}
|
||||
}
|
475
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_dumper.cc
vendored
Normal file
475
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_dumper.cc
vendored
Normal file
|
@ -0,0 +1,475 @@
|
|||
// 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.
|
||||
|
||||
// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
|
||||
// See linux_dumper.h for details.
|
||||
|
||||
// This code deals with the mechanics of getting information about a crashed
|
||||
// process. Since this code may run in a compromised address space, the same
|
||||
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
|
||||
// use the alternative allocator.
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "common/linux/elfutils.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
#include "common/linux/safe_readlink.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
||||
static const char kDeletedSuffix[] = " (deleted)";
|
||||
static const char kReservedFlags[] = " ---p";
|
||||
|
||||
inline static bool IsMappedFileOpenUnsafe(
|
||||
const google_breakpad::MappingInfo& mapping) {
|
||||
// It is unsafe to attempt to open a mapped file that lives under /dev,
|
||||
// because the semantics of the open may be driver-specific so we'd risk
|
||||
// hanging the crash dumper. And a file in /dev/ almost certainly has no
|
||||
// ELF file identifier anyways.
|
||||
return my_strncmp(mapping.name,
|
||||
kMappedFileUnsafePrefix,
|
||||
sizeof(kMappedFileUnsafePrefix) - 1) == 0;
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// All interesting auvx entry types are below AT_SYSINFO_EHDR
|
||||
#define AT_MAX AT_SYSINFO_EHDR
|
||||
|
||||
LinuxDumper::LinuxDumper(pid_t pid)
|
||||
: pid_(pid),
|
||||
crash_address_(0),
|
||||
crash_signal_(0),
|
||||
crash_thread_(pid),
|
||||
threads_(&allocator_, 8),
|
||||
mappings_(&allocator_),
|
||||
auxv_(&allocator_, AT_MAX + 1) {
|
||||
// The passed-in size to the constructor (above) is only a hint.
|
||||
// Must call .resize() to do actual initialization of the elements.
|
||||
auxv_.resize(AT_MAX + 1);
|
||||
}
|
||||
|
||||
LinuxDumper::~LinuxDumper() {
|
||||
}
|
||||
|
||||
bool LinuxDumper::Init() {
|
||||
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
uint8_t identifier[sizeof(MDGUID)]) {
|
||||
assert(!member || mapping_id < mappings_.size());
|
||||
my_memset(identifier, 0, sizeof(MDGUID));
|
||||
if (IsMappedFileOpenUnsafe(mapping))
|
||||
return false;
|
||||
|
||||
// Special-case linux-gate because it's not a real file.
|
||||
if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
|
||||
void* linux_gate = NULL;
|
||||
if (pid_ == sys_getpid()) {
|
||||
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
|
||||
} else {
|
||||
linux_gate = allocator_.Alloc(mapping.size);
|
||||
CopyFromProcess(linux_gate, pid_,
|
||||
reinterpret_cast<const void*>(mapping.start_addr),
|
||||
mapping.size);
|
||||
}
|
||||
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
|
||||
}
|
||||
|
||||
char filename[NAME_MAX];
|
||||
size_t filename_len = my_strlen(mapping.name);
|
||||
if (filename_len >= NAME_MAX) {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
my_memcpy(filename, mapping.name, filename_len);
|
||||
filename[filename_len] = '\0';
|
||||
bool filename_modified = HandleDeletedFileInMapping(filename);
|
||||
|
||||
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||
if (!mapped_file.data() || mapped_file.size() < SELFMAG)
|
||||
return false;
|
||||
|
||||
bool success =
|
||||
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||
if (success && member && filename_modified) {
|
||||
mappings_[mapping_id]->name[filename_len -
|
||||
sizeof(kDeletedSuffix) + 1] = '\0';
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool ElfFileSoNameFromMappedFile(
|
||||
const void* elf_base, char* soname, size_t soname_size) {
|
||||
if (!IsValidElf(elf_base)) {
|
||||
// Not ELF
|
||||
return false;
|
||||
}
|
||||
|
||||
const void* segment_start;
|
||||
size_t segment_size;
|
||||
int elf_class;
|
||||
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
|
||||
&segment_start, &segment_size, &elf_class)) {
|
||||
// No dynamic section
|
||||
return false;
|
||||
}
|
||||
|
||||
const void* dynstr_start;
|
||||
size_t dynstr_size;
|
||||
if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB,
|
||||
&dynstr_start, &dynstr_size, &elf_class)) {
|
||||
// No dynstr section
|
||||
return false;
|
||||
}
|
||||
|
||||
const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start);
|
||||
size_t dcount = segment_size / sizeof(ElfW(Dyn));
|
||||
for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) {
|
||||
if (dyn->d_tag == DT_SONAME) {
|
||||
const char* dynstr = static_cast<const char*>(dynstr_start);
|
||||
if (dyn->d_un.d_val >= dynstr_size) {
|
||||
// Beyond the end of the dynstr section
|
||||
return false;
|
||||
}
|
||||
const char* str = dynstr + dyn->d_un.d_val;
|
||||
const size_t maxsize = dynstr_size - dyn->d_un.d_val;
|
||||
my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Did not find SONAME
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the shared object name (SONAME) by examining the ELF information
|
||||
// for |mapping|. If the SONAME is found copy it into the passed buffer
|
||||
// |soname| and return true. The size of the buffer is |soname_size|.
|
||||
// The SONAME will be truncated if it is too long to fit in the buffer.
|
||||
bool ElfFileSoName(
|
||||
const MappingInfo& mapping, char* soname, size_t soname_size) {
|
||||
if (IsMappedFileOpenUnsafe(mapping)) {
|
||||
// Not safe
|
||||
return false;
|
||||
}
|
||||
|
||||
char filename[NAME_MAX];
|
||||
size_t filename_len = my_strlen(mapping.name);
|
||||
if (filename_len >= NAME_MAX) {
|
||||
assert(false);
|
||||
// name too long
|
||||
return false;
|
||||
}
|
||||
|
||||
my_memcpy(filename, mapping.name, filename_len);
|
||||
filename[filename_len] = '\0';
|
||||
|
||||
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||
if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
|
||||
// mmap failed
|
||||
return false;
|
||||
}
|
||||
|
||||
return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// static
|
||||
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
char* file_name,
|
||||
size_t file_name_size) {
|
||||
my_strlcpy(file_path, mapping.name, file_path_size);
|
||||
|
||||
// If an executable is mapped from a non-zero offset, this is likely because
|
||||
// the executable was loaded directly from inside an archive file (e.g., an
|
||||
// apk on Android). We try to find the name of the shared object (SONAME) by
|
||||
// looking in the file for ELF sections.
|
||||
bool mapped_from_archive = false;
|
||||
if (mapping.exec && mapping.offset != 0)
|
||||
mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size);
|
||||
|
||||
if (mapped_from_archive) {
|
||||
// Some tools (e.g., stackwalk) extract the basename from the pathname. In
|
||||
// this case, we append the file_name to the mapped archive path as follows:
|
||||
// file_name := libname.so
|
||||
// file_path := /path/to/ARCHIVE.APK/libname.so
|
||||
if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) {
|
||||
my_strlcat(file_path, "/", file_path_size);
|
||||
my_strlcat(file_path, file_name, file_path_size);
|
||||
}
|
||||
} else {
|
||||
// Common case:
|
||||
// file_path := /path/to/libname.so
|
||||
// file_name := libname.so
|
||||
const char* basename = my_strrchr(file_path, '/');
|
||||
basename = basename == NULL ? file_path : (basename + 1);
|
||||
my_strlcpy(file_name, basename, file_name_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool LinuxDumper::ReadAuxv() {
|
||||
char auxv_path[NAME_MAX];
|
||||
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = sys_open(auxv_path, O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
elf_aux_entry one_aux_entry;
|
||||
bool res = false;
|
||||
while (sys_read(fd,
|
||||
&one_aux_entry,
|
||||
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
|
||||
one_aux_entry.a_type != AT_NULL) {
|
||||
if (one_aux_entry.a_type <= AT_MAX) {
|
||||
auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
sys_close(fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool LinuxDumper::EnumerateMappings() {
|
||||
char maps_path[NAME_MAX];
|
||||
if (!BuildProcPath(maps_path, pid_, "maps"))
|
||||
return false;
|
||||
|
||||
// linux_gate_loc is the beginning of the kernel's mapping of
|
||||
// linux-gate.so in the process. It doesn't actually show up in the
|
||||
// maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
|
||||
// aux vector entry, which gives the information necessary to special
|
||||
// case its entry when creating the list of mappings.
|
||||
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
|
||||
// information.
|
||||
const void* linux_gate_loc =
|
||||
reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]);
|
||||
// Although the initial executable is usually the first mapping, it's not
|
||||
// guaranteed (see http://crosbug.com/25355); therefore, try to use the
|
||||
// actual entry point to find the mapping.
|
||||
const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]);
|
||||
|
||||
const int fd = sys_open(maps_path, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
LineReader* const line_reader = new(allocator_) LineReader(fd);
|
||||
|
||||
const char* line;
|
||||
unsigned line_len;
|
||||
while (line_reader->GetNextLine(&line, &line_len)) {
|
||||
uintptr_t start_addr, end_addr, offset;
|
||||
|
||||
const char* i1 = my_read_hex_ptr(&start_addr, line);
|
||||
if (*i1 == '-') {
|
||||
const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
|
||||
if (*i2 == ' ') {
|
||||
bool exec = (*(i2 + 3) == 'x');
|
||||
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
|
||||
if (*i3 == ' ') {
|
||||
const char* name = NULL;
|
||||
// Only copy name if the name is a valid path name, or if
|
||||
// it's the VDSO image.
|
||||
if (((name = my_strchr(line, '/')) == NULL) &&
|
||||
linux_gate_loc &&
|
||||
reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
|
||||
name = kLinuxGateLibraryName;
|
||||
offset = 0;
|
||||
}
|
||||
// Merge adjacent mappings with the same name into one module,
|
||||
// assuming they're a single library mapped by the dynamic linker
|
||||
if (name && !mappings_.empty()) {
|
||||
MappingInfo* module = mappings_.back();
|
||||
if ((start_addr == module->start_addr + module->size) &&
|
||||
(my_strlen(name) == my_strlen(module->name)) &&
|
||||
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
|
||||
module->size = end_addr - module->start_addr;
|
||||
line_reader->PopLine(line_len);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Also merge mappings that result from address ranges that the
|
||||
// linker reserved but which a loaded library did not use. These
|
||||
// appear as an anonymous private mapping with no access flags set
|
||||
// and which directly follow an executable mapping.
|
||||
if (!name && !mappings_.empty()) {
|
||||
MappingInfo* module = mappings_.back();
|
||||
if ((start_addr == module->start_addr + module->size) &&
|
||||
module->exec &&
|
||||
module->name[0] == '/' &&
|
||||
offset == 0 && my_strncmp(i2,
|
||||
kReservedFlags,
|
||||
sizeof(kReservedFlags) - 1) == 0) {
|
||||
module->size = end_addr - module->start_addr;
|
||||
line_reader->PopLine(line_len);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
MappingInfo* const module = new(allocator_) MappingInfo;
|
||||
my_memset(module, 0, sizeof(MappingInfo));
|
||||
module->start_addr = start_addr;
|
||||
module->size = end_addr - start_addr;
|
||||
module->offset = offset;
|
||||
module->exec = exec;
|
||||
if (name != NULL) {
|
||||
const unsigned l = my_strlen(name);
|
||||
if (l < sizeof(module->name))
|
||||
my_memcpy(module->name, name, l);
|
||||
}
|
||||
// If this is the entry-point mapping, and it's not already the
|
||||
// first one, then we need to make it be first. This is because
|
||||
// the minidump format assumes the first module is the one that
|
||||
// corresponds to the main executable (as codified in
|
||||
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
|
||||
if (entry_point_loc &&
|
||||
(entry_point_loc >=
|
||||
reinterpret_cast<void*>(module->start_addr)) &&
|
||||
(entry_point_loc <
|
||||
reinterpret_cast<void*>(module->start_addr+module->size)) &&
|
||||
!mappings_.empty()) {
|
||||
// push the module onto the front of the list.
|
||||
mappings_.resize(mappings_.size() + 1);
|
||||
for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
|
||||
mappings_[idx] = mappings_[idx - 1];
|
||||
mappings_[0] = module;
|
||||
} else {
|
||||
mappings_.push_back(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
line_reader->PopLine(line_len);
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
|
||||
return !mappings_.empty();
|
||||
}
|
||||
|
||||
// Get information about the stack, given the stack pointer. We don't try to
|
||||
// walk the stack since we might not have all the information needed to do
|
||||
// unwind. So we just grab, up to, 32k of stack.
|
||||
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
||||
uintptr_t int_stack_pointer) {
|
||||
// Move the stack pointer to the bottom of the page that it's in.
|
||||
const uintptr_t page_size = getpagesize();
|
||||
|
||||
uint8_t* const stack_pointer =
|
||||
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
|
||||
|
||||
// The number of bytes of stack which we try to capture.
|
||||
static const ptrdiff_t kStackToCapture = 32 * 1024;
|
||||
|
||||
const MappingInfo* mapping = FindMapping(stack_pointer);
|
||||
if (!mapping)
|
||||
return false;
|
||||
const ptrdiff_t offset = stack_pointer -
|
||||
reinterpret_cast<uint8_t*>(mapping->start_addr);
|
||||
const ptrdiff_t distance_to_end =
|
||||
static_cast<ptrdiff_t>(mapping->size) - offset;
|
||||
*stack_len = distance_to_end > kStackToCapture ?
|
||||
kStackToCapture : distance_to_end;
|
||||
*stack = stack_pointer;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the mapping which the given memory address falls in.
|
||||
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||
const uintptr_t addr = (uintptr_t) address;
|
||||
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
|
||||
if (addr >= start && addr - start < mappings_[i]->size)
|
||||
return mappings_[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
||||
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
|
||||
|
||||
// Check for ' (deleted)' in |path|.
|
||||
// |path| has to be at least as long as "/x (deleted)".
|
||||
const size_t path_len = my_strlen(path);
|
||||
if (path_len < kDeletedSuffixLen + 2)
|
||||
return false;
|
||||
if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix,
|
||||
kDeletedSuffixLen) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check |path| against the /proc/pid/exe 'symlink'.
|
||||
char exe_link[NAME_MAX];
|
||||
char new_path[NAME_MAX];
|
||||
if (!BuildProcPath(exe_link, pid_, "exe"))
|
||||
return false;
|
||||
if (!SafeReadLink(exe_link, new_path))
|
||||
return false;
|
||||
if (my_strcmp(path, new_path) != 0)
|
||||
return false;
|
||||
|
||||
// Check to see if someone actually named their executable 'foo (deleted)'.
|
||||
struct kernel_stat exe_stat;
|
||||
struct kernel_stat new_path_stat;
|
||||
if (sys_stat(exe_link, &exe_stat) == 0 &&
|
||||
sys_stat(new_path, &new_path_stat) == 0 &&
|
||||
exe_stat.st_dev == new_path_stat.st_dev &&
|
||||
exe_stat.st_ino == new_path_stat.st_ino) {
|
||||
return false;
|
||||
}
|
||||
|
||||
my_memcpy(path, exe_link, NAME_MAX);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
189
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_dumper.h
vendored
Normal file
189
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_dumper.h
vendored
Normal file
|
@ -0,0 +1,189 @@
|
|||
// 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.
|
||||
|
||||
// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
|
||||
// is a base class for extracting information of a crashed process. It
|
||||
// was originally a complete implementation using the ptrace API, but
|
||||
// has been refactored to allow derived implementations supporting both
|
||||
// ptrace and core dump. A portion of the original implementation is now
|
||||
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
|
||||
// details).
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
||||
|
||||
#include <elf.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/mapping_info.h"
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
#include "common/memory.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||
#if defined(__i386) || defined(__ARM_EABI__) || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABIO32)
|
||||
typedef Elf32_auxv_t elf_aux_entry;
|
||||
#elif defined(__x86_64) || defined(__aarch64__) || \
|
||||
(defined(__mips__) && _MIPS_SIM != _ABIO32)
|
||||
typedef Elf64_auxv_t elf_aux_entry;
|
||||
#endif
|
||||
|
||||
typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
||||
|
||||
// When we find the VDSO mapping in the process's address space, this
|
||||
// is the name we use for it when writing it to the minidump.
|
||||
// This should always be less than NAME_MAX!
|
||||
const char kLinuxGateLibraryName[] = "linux-gate.so";
|
||||
|
||||
class LinuxDumper {
|
||||
public:
|
||||
explicit LinuxDumper(pid_t pid);
|
||||
|
||||
virtual ~LinuxDumper();
|
||||
|
||||
// Parse the data for |threads| and |mappings|.
|
||||
virtual bool Init();
|
||||
|
||||
// Return true if the dumper performs a post-mortem dump.
|
||||
virtual bool IsPostMortem() const = 0;
|
||||
|
||||
// Suspend/resume all threads in the given process.
|
||||
virtual bool ThreadsSuspend() = 0;
|
||||
virtual bool ThreadsResume() = 0;
|
||||
|
||||
// Read information about the |index|-th thread of |threads_|.
|
||||
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
|
||||
|
||||
// These are only valid after a call to |Init|.
|
||||
const wasteful_vector<pid_t> &threads() { return threads_; }
|
||||
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
|
||||
const MappingInfo* FindMapping(const void* address) const;
|
||||
const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
|
||||
|
||||
// Find a block of memory to take as the stack given the top of stack pointer.
|
||||
// stack: (output) the lowest address in the memory area
|
||||
// stack_len: (output) the length of the memory area
|
||||
// stack_top: the current top of the stack
|
||||
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
|
||||
|
||||
PageAllocator* allocator() { return &allocator_; }
|
||||
|
||||
// Copy content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|. Returns true on success.
|
||||
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length) = 0;
|
||||
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
// |path| is a character array of at least NAME_MAX bytes to return the
|
||||
// result.|node| is the final node without any slashes. Returns true on
|
||||
// success.
|
||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
|
||||
|
||||
// Generate a File ID from the .text section of a mapped entry.
|
||||
// If not a member, mapping_id is ignored. This method can also manipulate the
|
||||
// |mapping|.name to truncate "(deleted)" from the file name if necessary.
|
||||
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
uint8_t identifier[sizeof(MDGUID)]);
|
||||
|
||||
uintptr_t crash_address() const { return crash_address_; }
|
||||
void set_crash_address(uintptr_t crash_address) {
|
||||
crash_address_ = crash_address;
|
||||
}
|
||||
|
||||
int crash_signal() const { return crash_signal_; }
|
||||
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
|
||||
|
||||
pid_t crash_thread() const { return crash_thread_; }
|
||||
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
|
||||
|
||||
// Extracts the effective path and file name of from |mapping|. In most cases
|
||||
// the effective name/path are just the mapping's path and basename. In some
|
||||
// other cases, however, a library can be mapped from an archive (e.g., when
|
||||
// loading .so libs from an apk on Android) and this method is able to
|
||||
// reconstruct the original file name.
|
||||
static void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
char* file_name,
|
||||
size_t file_name_size);
|
||||
|
||||
protected:
|
||||
bool ReadAuxv();
|
||||
|
||||
virtual bool EnumerateMappings();
|
||||
|
||||
virtual bool EnumerateThreads() = 0;
|
||||
|
||||
// For the case where a running program has been deleted, it'll show up in
|
||||
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
|
||||
// see if '/path/to/program (deleted)' matches /proc/pid/exe and return
|
||||
// /proc/pid/exe in |path| so ELF identifier generation works correctly. This
|
||||
// also checks to see if '/path/to/program (deleted)' exists, so it does not
|
||||
// get fooled by a poorly named binary.
|
||||
// For programs that don't end with ' (deleted)', this is a no-op.
|
||||
// This assumes |path| is a buffer with length NAME_MAX.
|
||||
// Returns true if |path| is modified.
|
||||
bool HandleDeletedFileInMapping(char* path) const;
|
||||
|
||||
// ID of the crashed process.
|
||||
const pid_t pid_;
|
||||
|
||||
// Virtual address at which the process crashed.
|
||||
uintptr_t crash_address_;
|
||||
|
||||
// Signal that terminated the crashed process.
|
||||
int crash_signal_;
|
||||
|
||||
// ID of the crashed thread.
|
||||
pid_t crash_thread_;
|
||||
|
||||
mutable PageAllocator allocator_;
|
||||
|
||||
// IDs of all the threads.
|
||||
wasteful_vector<pid_t> threads_;
|
||||
|
||||
// Info from /proc/<pid>/maps.
|
||||
wasteful_vector<MappingInfo*> mappings_;
|
||||
|
||||
// Info from /proc/<pid>/auxv
|
||||
wasteful_vector<elf_aux_val_t> auxv_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
|
|
@ -0,0 +1,94 @@
|
|||
// 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.
|
||||
//
|
||||
// Helper program for the linux_dumper class, which creates a bunch of
|
||||
// threads. The first word of each thread's stack is set to the thread
|
||||
// id.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
#if defined(__ARM_EABI__)
|
||||
#define TID_PTR_REGISTER "r3"
|
||||
#elif defined(__aarch64__)
|
||||
#define TID_PTR_REGISTER "x3"
|
||||
#elif defined(__i386)
|
||||
#define TID_PTR_REGISTER "ecx"
|
||||
#elif defined(__x86_64)
|
||||
#define TID_PTR_REGISTER "rcx"
|
||||
#elif defined(__mips__)
|
||||
#define TID_PTR_REGISTER "$1"
|
||||
#else
|
||||
#error This test has not been ported to this platform.
|
||||
#endif
|
||||
|
||||
void *thread_function(void *data) {
|
||||
int pipefd = *static_cast<int *>(data);
|
||||
volatile pid_t thread_id = syscall(__NR_gettid);
|
||||
// Signal parent that a thread has started.
|
||||
uint8_t byte = 1;
|
||||
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
|
||||
perror("ERROR: parent notification failed");
|
||||
return NULL;
|
||||
}
|
||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
|
||||
while (true)
|
||||
asm volatile ("" : : "r" (thread_id_ptr));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr,
|
||||
"usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n");
|
||||
return 1;
|
||||
}
|
||||
int pipefd = atoi(argv[1]);
|
||||
int num_threads = atoi(argv[2]);
|
||||
if (num_threads < 1) {
|
||||
fprintf(stderr, "ERROR: number of threads is 0");
|
||||
return 1;
|
||||
}
|
||||
google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]);
|
||||
pthread_attr_t thread_attributes;
|
||||
pthread_attr_init(&thread_attributes);
|
||||
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
|
||||
for (int i = 1; i < num_threads; i++) {
|
||||
pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd);
|
||||
}
|
||||
thread_function(&pipefd);
|
||||
return 0;
|
||||
}
|
349
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
vendored
Normal file
349
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
vendored
Normal file
|
@ -0,0 +1,349 @@
|
|||
// 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.
|
||||
|
||||
// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
|
||||
// See linux_ptrace_dumper.h for detals.
|
||||
// This class was originally splitted from google_breakpad::LinuxDumper.
|
||||
|
||||
// This code deals with the mechanics of getting information about a crashed
|
||||
// process. Since this code may run in a compromised address space, the same
|
||||
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
|
||||
// use the alternative allocator.
|
||||
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if defined(__i386)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
#include "client/linux/minidump_writer/directory_reader.h"
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
// Suspends a thread by attaching to it.
|
||||
static bool SuspendThread(pid_t pid) {
|
||||
// This may fail if the thread has just died or debugged.
|
||||
errno = 0;
|
||||
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
|
||||
errno != 0) {
|
||||
return false;
|
||||
}
|
||||
while (sys_waitpid(pid, NULL, __WALL) < 0) {
|
||||
if (errno != EINTR) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
// On x86, the stack pointer is NULL or -1, when executing trusted code in
|
||||
// the seccomp sandbox. Not only does this cause difficulties down the line
|
||||
// when trying to dump the thread's stack, it also results in the minidumps
|
||||
// containing information about the trusted threads. This information is
|
||||
// generally completely meaningless and just pollutes the minidumps.
|
||||
// We thus test the stack pointer and exclude any threads that are part of
|
||||
// the seccomp sandbox's trusted code.
|
||||
user_regs_struct regs;
|
||||
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 ||
|
||||
#if defined(__i386)
|
||||
!regs.esp
|
||||
#elif defined(__x86_64)
|
||||
!regs.rsp
|
||||
#endif
|
||||
) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resumes a thread by detaching from it.
|
||||
static bool ResumeThread(pid_t pid) {
|
||||
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
|
||||
: LinuxDumper(pid),
|
||||
threads_suspended_(false) {
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
|
||||
const char* node) const {
|
||||
if (!path || !node || pid <= 0)
|
||||
return false;
|
||||
|
||||
size_t node_len = my_strlen(node);
|
||||
if (node_len == 0)
|
||||
return false;
|
||||
|
||||
const unsigned pid_len = my_uint_len(pid);
|
||||
const size_t total_length = 6 + pid_len + 1 + node_len;
|
||||
if (total_length >= NAME_MAX)
|
||||
return false;
|
||||
|
||||
my_memcpy(path, "/proc/", 6);
|
||||
my_uitos(path + 6, pid, pid_len);
|
||||
path[6 + pid_len] = '/';
|
||||
my_memcpy(path + 6 + pid_len + 1, node, node_len);
|
||||
path[total_length] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
const void* src, size_t length) {
|
||||
unsigned long tmp = 55;
|
||||
size_t done = 0;
|
||||
static const size_t word_size = sizeof(tmp);
|
||||
uint8_t* const local = (uint8_t*) dest;
|
||||
uint8_t* const remote = (uint8_t*) src;
|
||||
|
||||
while (done < length) {
|
||||
const size_t l = (length - done > word_size) ? word_size : (length - done);
|
||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
|
||||
tmp = 0;
|
||||
}
|
||||
my_memcpy(local + done, &tmp, l);
|
||||
done += l;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
|
||||
// these members are set to -1. Returns true iff all three members are
|
||||
// available.
|
||||
bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
if (index >= threads_.size())
|
||||
return false;
|
||||
|
||||
pid_t tid = threads_[index];
|
||||
|
||||
assert(info != NULL);
|
||||
char status_path[NAME_MAX];
|
||||
if (!BuildProcPath(status_path, tid, "status"))
|
||||
return false;
|
||||
|
||||
const int fd = sys_open(status_path, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
LineReader* const line_reader = new(allocator_) LineReader(fd);
|
||||
const char* line;
|
||||
unsigned line_len;
|
||||
|
||||
info->ppid = info->tgid = -1;
|
||||
|
||||
while (line_reader->GetNextLine(&line, &line_len)) {
|
||||
if (my_strncmp("Tgid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->tgid, line + 6);
|
||||
} else if (my_strncmp("PPid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->ppid, line + 6);
|
||||
}
|
||||
|
||||
line_reader->PopLine(line_len);
|
||||
}
|
||||
sys_close(fd);
|
||||
|
||||
if (info->ppid == -1 || info->tgid == -1)
|
||||
return false;
|
||||
|
||||
#ifdef PTRACE_GETREGSET
|
||||
struct iovec io;
|
||||
info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
|
||||
return false;
|
||||
}
|
||||
#else // PTRACE_GETREGSET
|
||||
void* gp_addr;
|
||||
info->GetGeneralPurposeRegisters(&gp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* fp_addr;
|
||||
info->GetFloatingPointRegisters(&fp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__i386)
|
||||
#if !defined(bit_FXSAVE) // e.g. Clang
|
||||
#define bit_FXSAVE bit_FXSR
|
||||
#endif
|
||||
// Detect if the CPU supports the FXSAVE/FXRSTOR instructions
|
||||
int eax, ebx, ecx, edx;
|
||||
__cpuid(1, eax, ebx, ecx, edx);
|
||||
if (edx & bit_FXSAVE) {
|
||||
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
memset(&info->fpxregs, 0, sizeof(info->fpxregs));
|
||||
}
|
||||
#endif // defined(__i386)
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
|
||||
if (sys_ptrace(
|
||||
PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*> (offsetof(struct user,
|
||||
u_debugreg[0]) + i *
|
||||
sizeof(debugreg_t)),
|
||||
&info->dregs[i]) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__mips__)
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 1), &info->mcontext.lo1);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 2), &info->mcontext.hi2);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 3), &info->mcontext.lo2);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 4), &info->mcontext.hi3);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 5), &info->mcontext.lo3);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp);
|
||||
#endif
|
||||
|
||||
const uint8_t* stack_pointer;
|
||||
#if defined(__i386)
|
||||
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||
#elif defined(__x86_64)
|
||||
my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||
#elif defined(__ARM_EABI__)
|
||||
my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||
#elif defined(__aarch64__)
|
||||
my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
||||
#elif defined(__mips__)
|
||||
stack_pointer =
|
||||
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::IsPostMortem() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::ThreadsSuspend() {
|
||||
if (threads_suspended_)
|
||||
return true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||
if (!SuspendThread(threads_[i])) {
|
||||
// If the thread either disappeared before we could attach to it, or if
|
||||
// it was part of the seccomp sandbox's trusted code, it is OK to
|
||||
// silently drop it from the minidump.
|
||||
if (i < threads_.size() - 1) {
|
||||
my_memmove(&threads_[i], &threads_[i + 1],
|
||||
(threads_.size() - i - 1) * sizeof(threads_[i]));
|
||||
}
|
||||
threads_.resize(threads_.size() - 1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
threads_suspended_ = true;
|
||||
return threads_.size() > 0;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::ThreadsResume() {
|
||||
if (!threads_suspended_)
|
||||
return false;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= ResumeThread(threads_[i]);
|
||||
threads_suspended_ = false;
|
||||
return good;
|
||||
}
|
||||
|
||||
// Parse /proc/$pid/task to list all the threads of the process identified by
|
||||
// pid.
|
||||
bool LinuxPtraceDumper::EnumerateThreads() {
|
||||
char task_path[NAME_MAX];
|
||||
if (!BuildProcPath(task_path, pid_, "task"))
|
||||
return false;
|
||||
|
||||
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
|
||||
|
||||
// The directory may contain duplicate entries which we filter by assuming
|
||||
// that they are consecutive.
|
||||
int last_tid = -1;
|
||||
const char* dent_name;
|
||||
while (dir_reader->GetNextEntry(&dent_name)) {
|
||||
if (my_strcmp(dent_name, ".") &&
|
||||
my_strcmp(dent_name, "..")) {
|
||||
int tid = 0;
|
||||
if (my_strtoui(&tid, dent_name) &&
|
||||
last_tid != tid) {
|
||||
last_tid = tid;
|
||||
threads_.push_back(tid);
|
||||
}
|
||||
}
|
||||
dir_reader->PopEntry();
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
92
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h
vendored
Normal file
92
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
// 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.
|
||||
|
||||
// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
|
||||
// class, which is derived from google_breakpad::LinuxDumper to extract
|
||||
// information from a crashed process via ptrace.
|
||||
// This class was originally splitted from google_breakpad::LinuxDumper.
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class LinuxPtraceDumper : public LinuxDumper {
|
||||
public:
|
||||
// Constructs a dumper for extracting information of a given process
|
||||
// with a process ID of |pid|.
|
||||
explicit LinuxPtraceDumper(pid_t pid);
|
||||
|
||||
// Implements LinuxDumper::BuildProcPath().
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
// |path| is a character array of at least NAME_MAX bytes to return the
|
||||
// result. |node| is the final node without any slashes. Returns true on
|
||||
// success.
|
||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
|
||||
|
||||
// Implements LinuxDumper::CopyFromProcess().
|
||||
// Copies content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|. This method uses ptrace to extract
|
||||
// the content from the target process. Always returns true.
|
||||
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length);
|
||||
|
||||
// Implements LinuxDumper::GetThreadInfoByIndex().
|
||||
// Reads information about the |index|-th thread of |threads_|.
|
||||
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
|
||||
|
||||
// Implements LinuxDumper::IsPostMortem().
|
||||
// Always returns false to indicate this dumper performs a dump of
|
||||
// a crashed process via ptrace.
|
||||
virtual bool IsPostMortem() const;
|
||||
|
||||
// Implements LinuxDumper::ThreadsSuspend().
|
||||
// Suspends all threads in the given process. Returns true on success.
|
||||
virtual bool ThreadsSuspend();
|
||||
|
||||
// Implements LinuxDumper::ThreadsResume().
|
||||
// Resumes all threads in the given process. Returns true on success.
|
||||
virtual bool ThreadsResume();
|
||||
|
||||
protected:
|
||||
// Implements LinuxDumper::EnumerateThreads().
|
||||
// Enumerates all threads of the given process into |threads_|.
|
||||
virtual bool EnumerateThreads();
|
||||
|
||||
private:
|
||||
// Set to true if all threads of the crashed process are suspended.
|
||||
bool threads_suspended_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_
|
|
@ -0,0 +1,463 @@
|
|||
// Copyright (c) 2009, 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.
|
||||
|
||||
// linux_ptrace_dumper_unittest.cc:
|
||||
// Unit tests for google_breakpad::LinuxPtraceDumper.
|
||||
//
|
||||
// This file was renamed from linux_dumper_unittest.cc and modified due
|
||||
// to LinuxDumper being splitted into two classes.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/linux/safe_readlink.h"
|
||||
#include "common/memory.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
#ifndef PR_SET_PTRACER
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test LinuxPtraceDumperTest;
|
||||
|
||||
/* Fixture for running tests in a child process. */
|
||||
class LinuxPtraceDumperChildTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
child_pid_ = fork();
|
||||
#ifndef __ANDROID__
|
||||
prctl(PR_SET_PTRACER, child_pid_);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Gtest is calling TestBody from this class, which sets up a child
|
||||
* process in which the RealTestBody virtual member is called.
|
||||
* As such, TestBody is not supposed to be overridden in derived classes.
|
||||
*/
|
||||
virtual void TestBody() /* final */ {
|
||||
if (child_pid_ == 0) {
|
||||
// child process
|
||||
RealTestBody();
|
||||
exit(HasFatalFailure() ? kFatalFailure :
|
||||
(HasNonfatalFailure() ? kNonFatalFailure : 0));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(child_pid_ > 0);
|
||||
int status;
|
||||
waitpid(child_pid_, &status, 0);
|
||||
if (WEXITSTATUS(status) == kFatalFailure) {
|
||||
GTEST_FATAL_FAILURE_("Test failed in child process");
|
||||
} else if (WEXITSTATUS(status) == kNonFatalFailure) {
|
||||
GTEST_NONFATAL_FAILURE_("Test failed in child process");
|
||||
}
|
||||
}
|
||||
|
||||
/* Gtest defines TestBody functions through its macros, but classes
|
||||
* derived from this one need to define RealTestBody instead.
|
||||
* This is achieved by defining a TestBody macro further below.
|
||||
*/
|
||||
virtual void RealTestBody() = 0;
|
||||
private:
|
||||
static const int kFatalFailure = 1;
|
||||
static const int kNonFatalFailure = 2;
|
||||
|
||||
pid_t child_pid_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/* Replace TestBody declarations within TEST*() with RealTestBody
|
||||
* declarations */
|
||||
#define TestBody RealTestBody
|
||||
|
||||
TEST_F(LinuxPtraceDumperChildTest, Setup) {
|
||||
LinuxPtraceDumper dumper(getppid());
|
||||
}
|
||||
|
||||
TEST_F(LinuxPtraceDumperChildTest, FindMappings) {
|
||||
LinuxPtraceDumper dumper(getppid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
|
||||
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
|
||||
ASSERT_FALSE(dumper.FindMapping(NULL));
|
||||
}
|
||||
|
||||
TEST_F(LinuxPtraceDumperChildTest, ThreadList) {
|
||||
LinuxPtraceDumper dumper(getppid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
ASSERT_GE(dumper.threads().size(), (size_t)1);
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < dumper.threads().size(); ++i) {
|
||||
if (dumper.threads()[i] == getppid()) {
|
||||
ASSERT_FALSE(found);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found);
|
||||
}
|
||||
|
||||
// Helper stack class to close a file descriptor and unmap
|
||||
// a mmap'ed mapping.
|
||||
class StackHelper {
|
||||
public:
|
||||
StackHelper()
|
||||
: fd_(-1), mapping_(NULL), size_(0) {}
|
||||
~StackHelper() {
|
||||
if (size_)
|
||||
munmap(mapping_, size_);
|
||||
if (fd_ >= 0)
|
||||
close(fd_);
|
||||
}
|
||||
void Init(int fd, char* mapping, size_t size) {
|
||||
fd_ = fd;
|
||||
mapping_ = mapping;
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
char* mapping() const { return mapping_; }
|
||||
size_t size() const { return size_; }
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
char* mapping_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest {
|
||||
protected:
|
||||
virtual void SetUp();
|
||||
|
||||
string helper_path_;
|
||||
size_t page_size_;
|
||||
StackHelper helper_;
|
||||
};
|
||||
|
||||
void LinuxPtraceDumperMappingsTest::SetUp() {
|
||||
helper_path_ = GetHelperBinary();
|
||||
if (helper_path_.empty()) {
|
||||
FAIL() << "Couldn't find helper binary";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// mmap two segments out of the helper binary, one
|
||||
// enclosed in the other, but with different protections.
|
||||
page_size_ = sysconf(_SC_PAGESIZE);
|
||||
const size_t kMappingSize = 3 * page_size_;
|
||||
int fd = open(helper_path_.c_str(), O_RDONLY);
|
||||
ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_
|
||||
<< ", Error: " << strerror(errno);
|
||||
char* mapping =
|
||||
reinterpret_cast<char*>(mmap(NULL,
|
||||
kMappingSize,
|
||||
PROT_READ,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0));
|
||||
ASSERT_TRUE(mapping);
|
||||
|
||||
// Ensure that things get cleaned up.
|
||||
helper_.Init(fd, mapping, kMappingSize);
|
||||
|
||||
// Carve a page out of the first mapping with different permissions.
|
||||
char* inside_mapping = reinterpret_cast<char*>(
|
||||
mmap(mapping + 2 * page_size_,
|
||||
page_size_,
|
||||
PROT_NONE,
|
||||
MAP_SHARED | MAP_FIXED,
|
||||
fd,
|
||||
// Map a different offset just to
|
||||
// better test real-world conditions.
|
||||
page_size_));
|
||||
ASSERT_TRUE(inside_mapping);
|
||||
|
||||
LinuxPtraceDumperChildTest::SetUp();
|
||||
}
|
||||
|
||||
TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) {
|
||||
// Now check that LinuxPtraceDumper interpreted the mappings properly.
|
||||
LinuxPtraceDumper dumper(getppid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
int mapping_count = 0;
|
||||
for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
|
||||
const MappingInfo& mapping = *dumper.mappings()[i];
|
||||
if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) {
|
||||
// This mapping should encompass the entire original mapped
|
||||
// range.
|
||||
EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()),
|
||||
mapping.start_addr);
|
||||
EXPECT_EQ(this->helper_.size(), mapping.size);
|
||||
EXPECT_EQ(0U, mapping.offset);
|
||||
mapping_count++;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(1, mapping_count);
|
||||
}
|
||||
|
||||
TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) {
|
||||
const pid_t pid = getppid();
|
||||
LinuxPtraceDumper dumper(pid);
|
||||
|
||||
char maps_path[NAME_MAX] = "";
|
||||
char maps_path_expected[NAME_MAX];
|
||||
snprintf(maps_path_expected, sizeof(maps_path_expected),
|
||||
"/proc/%d/maps", pid);
|
||||
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
|
||||
EXPECT_STREQ(maps_path_expected, maps_path);
|
||||
|
||||
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
|
||||
|
||||
char long_node[NAME_MAX];
|
||||
size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
|
||||
memset(long_node, 'a', long_node_len);
|
||||
long_node[long_node_len] = '\0';
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
|
||||
}
|
||||
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
// Ensure that the linux-gate VDSO is included in the mapping list.
|
||||
TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) {
|
||||
LinuxPtraceDumper dumper(getppid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
void* linux_gate_loc =
|
||||
reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]);
|
||||
ASSERT_TRUE(linux_gate_loc);
|
||||
bool found_linux_gate = false;
|
||||
|
||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
||||
const MappingInfo* mapping;
|
||||
for (unsigned i = 0; i < mappings.size(); ++i) {
|
||||
mapping = mappings[i];
|
||||
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
|
||||
found_linux_gate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found_linux_gate);
|
||||
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
|
||||
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
|
||||
}
|
||||
|
||||
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
|
||||
TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
|
||||
LinuxPtraceDumper dumper(getppid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
bool found_linux_gate = false;
|
||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
||||
unsigned index = 0;
|
||||
for (unsigned i = 0; i < mappings.size(); ++i) {
|
||||
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
|
||||
found_linux_gate = true;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_linux_gate);
|
||||
|
||||
// Need to suspend the child so ptrace actually works.
|
||||
ASSERT_TRUE(dumper.ThreadsSuspend());
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
|
||||
true,
|
||||
index,
|
||||
identifier));
|
||||
uint8_t empty_identifier[sizeof(MDGUID)];
|
||||
memset(empty_identifier, 0, sizeof(empty_identifier));
|
||||
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
|
||||
EXPECT_TRUE(dumper.ThreadsResume());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
|
||||
// Calculate the File ID of our binary using both
|
||||
// FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
|
||||
// and ensure that we get the same result from both.
|
||||
char exe_name[PATH_MAX];
|
||||
ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
|
||||
|
||||
LinuxPtraceDumper dumper(getppid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
||||
bool found_exe = false;
|
||||
unsigned i;
|
||||
for (i = 0; i < mappings.size(); ++i) {
|
||||
const MappingInfo* mapping = mappings[i];
|
||||
if (!strcmp(mapping->name, exe_name)) {
|
||||
found_exe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_exe);
|
||||
|
||||
uint8_t identifier1[sizeof(MDGUID)];
|
||||
uint8_t identifier2[sizeof(MDGUID)];
|
||||
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
|
||||
identifier1));
|
||||
FileID fileid(exe_name);
|
||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
|
||||
char identifier_string1[37];
|
||||
char identifier_string2[37];
|
||||
FileID::ConvertIdentifierToString(identifier1, identifier_string1,
|
||||
37);
|
||||
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
|
||||
37);
|
||||
EXPECT_STREQ(identifier_string1, identifier_string2);
|
||||
}
|
||||
|
||||
/* Get back to normal behavior of TEST*() macros wrt TestBody. */
|
||||
#undef TestBody
|
||||
|
||||
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
static const int kNumberOfThreadsInHelperProgram = 5;
|
||||
char kNumberOfThreadsArgument[2];
|
||||
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
|
||||
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// In child process.
|
||||
close(fds[0]);
|
||||
|
||||
string helper_path(GetHelperBinary());
|
||||
if (helper_path.empty()) {
|
||||
FAIL() << "Couldn't find helper binary";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Pass the pipe fd and the number of threads as arguments.
|
||||
char pipe_fd_string[8];
|
||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
||||
execl(helper_path.c_str(),
|
||||
"linux_dumper_unittest_helper",
|
||||
pipe_fd_string,
|
||||
kNumberOfThreadsArgument,
|
||||
NULL);
|
||||
// Kill if we get here.
|
||||
printf("Errno from exec: %d", errno);
|
||||
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
|
||||
exit(0);
|
||||
}
|
||||
close(fds[1]);
|
||||
|
||||
// Wait for all child threads to indicate that they have started
|
||||
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
|
||||
struct pollfd pfd;
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = fds[0];
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
|
||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
||||
ASSERT_EQ(1, r);
|
||||
ASSERT_TRUE(pfd.revents & POLLIN);
|
||||
uint8_t junk;
|
||||
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
|
||||
static_cast<ssize_t>(sizeof(junk)));
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
// There is a race here because we may stop a child thread before
|
||||
// it is actually running the busy loop. Empirically this sleep
|
||||
// is sufficient to avoid the race.
|
||||
usleep(100000);
|
||||
|
||||
// Children are ready now.
|
||||
LinuxPtraceDumper dumper(child_pid);
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
|
||||
EXPECT_TRUE(dumper.ThreadsSuspend());
|
||||
|
||||
ThreadInfo one_thread;
|
||||
for (size_t i = 0; i < dumper.threads().size(); ++i) {
|
||||
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
|
||||
one_thread.stack_pointer));
|
||||
// In the helper program, we stored a pointer to the thread id in a
|
||||
// specific register. Check that we can recover its value.
|
||||
#if defined(__ARM_EABI__)
|
||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]);
|
||||
#elif defined(__aarch64__)
|
||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]);
|
||||
#elif defined(__i386)
|
||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx);
|
||||
#elif defined(__x86_64)
|
||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
|
||||
#elif defined(__mips__)
|
||||
pid_t* process_tid_location =
|
||||
reinterpret_cast<pid_t*>(one_thread.mcontext.gregs[1]);
|
||||
#else
|
||||
#error This test has not been ported to this platform.
|
||||
#endif
|
||||
pid_t one_thread_id;
|
||||
dumper.CopyFromProcess(&one_thread_id,
|
||||
dumper.threads()[i],
|
||||
process_tid_location,
|
||||
4);
|
||||
EXPECT_EQ(dumper.threads()[i], one_thread_id);
|
||||
}
|
||||
EXPECT_TRUE(dumper.ThreadsResume());
|
||||
kill(child_pid, SIGKILL);
|
||||
|
||||
// Reap child
|
||||
int status;
|
||||
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
|
||||
ASSERT_TRUE(WIFSIGNALED(status));
|
||||
ASSERT_EQ(SIGKILL, WTERMSIG(status));
|
||||
}
|
1373
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/minidump_writer.cc
vendored
Normal file
1373
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/minidump_writer.cc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
124
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/minidump_writer.h
vendored
Normal file
124
TMessagesProj/jni/third_party/breakpad/src/client/linux/minidump_writer/minidump_writer.h
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright (c) 2009, 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_MINIDUMP_WRITER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExceptionHandler;
|
||||
|
||||
#if defined(__aarch64__)
|
||||
typedef struct fpsimd_context fpstate_t;
|
||||
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
typedef struct _libc_fpstate fpstate_t;
|
||||
#endif
|
||||
|
||||
// These entries store a list of memory regions that the client wants included
|
||||
// in the minidump.
|
||||
struct AppMemory {
|
||||
void* ptr;
|
||||
size_t length;
|
||||
|
||||
bool operator==(const struct AppMemory& other) const {
|
||||
return ptr == other.ptr;
|
||||
}
|
||||
|
||||
bool operator==(const void* other) const {
|
||||
return ptr == other;
|
||||
}
|
||||
};
|
||||
typedef std::list<AppMemory> AppMemoryList;
|
||||
|
||||
// Writes a minidump to the filesystem. 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.
|
||||
// minidump_path: the path to the file to write to. This is opened O_EXCL and
|
||||
// fails open fails.
|
||||
// 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
|
||||
//
|
||||
// Returns true iff successful.
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
// Same as above but takes an open file descriptor instead of a path.
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
|
||||
// Alternate form of WriteMinidump() that works with processes that
|
||||
// are not expected to have crashed. If |process_blamed_thread| is
|
||||
// meaningful, it will be the one from which a crash signature is
|
||||
// extracted. It is not expected that this function will be called
|
||||
// from a compromised context, but it is safe to do so.
|
||||
bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
pid_t process_blamed_thread);
|
||||
|
||||
// These overloads also allow passing a list of known mappings and
|
||||
// a list of additional memory regions to be included in the minidump.
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
|
||||
// These overloads also allow passing a file size limit for the minidump.
|
||||
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
|
||||
bool WriteMinidump(const char* filename,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata,
|
||||
LinuxDumper* dumper);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue