Compare commits

...

654 Commits

Author SHA1 Message Date
luvletter2333 e72c1741bf
fix send logs 2023-03-28 08:51:10 +08:00
luvletter2333 6a5b90ed4b
bump version to 9.5.6-preview01 2023-03-28 08:29:02 +08:00
luvletter2333 f469c7fef9
fix: disable vibration 2023-03-28 08:09:18 +08:00
luvletter2333 eb8eb1474d
fix action bar header items 2023-03-28 08:02:27 +08:00
luvletter2333 c0fdbb58c0
refine action bar menus 2023-03-28 07:56:12 +08:00
luvletter2333 e63f66f505
remove help with NekoXBot when creating groups without inviting others 2023-03-28 06:35:50 +08:00
luvletter2333 71b3bb5e01
native: update ffmpeg to 4.4.3 2023-03-28 06:28:13 +08:00
luvletter2333 13d2187bdb
merge: official 9.5.6 2023-03-28 06:11:26 +08:00
luvletter2333 92e5043254
fix: merge emojis from tfoss 2023-03-28 05:49:01 +08:00
luvletter2333 4799b030b4
fix: merge bugs 2023-03-28 05:42:24 +08:00
Luv Letter e392d0db9b
ci: fix release scripts 2023-03-28 05:13:56 +08:00
luvletter2333 4933c7107b
wip: merge official 9.5.3 2023-03-28 05:12:27 +08:00
xaxtix bab9943ee0 update to 9.5.6 2023-03-24 17:34:35 +04:00
xaxtix 5e5b7659cb update to 9.5.5 2023-03-24 15:38:14 +04:00
xaxtix c06f56dcad update to 9.5.4 2023-03-18 17:33:38 +04:00
luvletter2333 6d0964f995
bump version to 9.3.3 2023-03-16 06:38:16 +08:00
luvletter2333 cf7001b2fc
feat: use Telegram API as a translator 2023-03-16 06:23:09 +08:00
luvletter2333 733feb011e
fix telegram translate crash on polls 2023-03-16 05:20:43 +08:00
luvletter2333 1e59e4e335
fix: repeat in topics 2023-03-16 05:18:45 +08:00
luvletter2333 da6d0bea22
fix: don't show fwd header in Replies 2023-03-16 03:34:50 +08:00
luvletter2333 1063f56a25
fix: show sender's name rather than forwarded's name in message reply 2023-03-16 02:41:08 +08:00
luvletter2333 c068a74dd8
fix: remember all clicked replies 2023-03-16 02:34:02 +08:00
xaxtix 07a2c9a330 update to 9.5.3 2023-03-14 02:39:50 +04:00
xaxtix 1dcd153fe2 update to 9.5.2 2023-03-11 00:20:59 +04:00
xaxtix 1a48adbec4 update to 9.5.1 2023-03-09 03:57:14 +04:00
xaxtix d374d4875d update to 9.5.0 2023-03-08 11:27:18 +04:00
luvletter2333 021af48ee3
test ci: include commit link in CI channel message 2023-03-07 02:02:59 +08:00
luvletter2333 ed4fdf2fe3
fix: ColoredImageSpan icon in invite link 2023-03-06 14:11:07 +08:00
luvletter2333 14e962d30a
fix: edit avatar & delete icon 2023-03-06 13:50:55 +08:00
luvletter2333 cedce6813e
fix: crash on 32+ accounts 2023-03-04 07:07:12 +08:00
luvletter2333 afe1f215c8
fix: ChatActivity link longpress 2023-03-04 07:06:45 +08:00
luvletter2333 0ee46c7b1c
fix ProfileActivity avatar and edit button 2023-03-04 07:06:17 +08:00
luvletter2333 1497c7ddd3
fix can't login account after logout 2023-03-04 06:58:26 +08:00
luvletter2333 d0bf3ef05b
encapsulate NekoX's BottomSheet into BottomSheet.Builder interface 2023-03-04 06:53:39 +08:00
luvletter2333 7fe115e83c
ci: fix release script 2023-03-04 06:49:31 +08:00
luvletter2333 7f43e7f173
ci: test release script 2023-03-04 05:29:39 +08:00
xaxtix 1ac56c671e update to 9.4.9 2023-02-28 23:33:54 +04:00
xaxtix 8bb3438f62 update to 9.4.8 2023-02-27 14:35:15 +04:00
xaxtix 7554c9b726 update to 9.4.7 2023-02-27 09:38:13 +04:00
xaxtix 116092e32d update to 9.4.6 2023-02-26 06:55:06 +04:00
xaxtix 810bc4ae67 update to 9.4.5 2023-02-25 12:01:39 +04:00
xaxtix 51e9947527 update to 9.4.3 2023-02-19 01:24:25 +04:00
xaxtix c48aa4f3c9 update to 9.4.2 2023-02-06 15:08:24 +04:00
xaxtix fb2d9f90bc update to 9.4.1 2023-02-04 20:13:50 +04:00
xaxtix 135e3cc766 update to 9.4.0 2023-02-03 23:11:36 +04:00
xaxtix 2628a58147 update apkdiff.py apkfrombundle.py 2023-01-26 17:06:12 +04:00
xaxtix 17283cea01 update gradle.properties 2023-01-25 19:17:47 +04:00
xaxtix f7f9363423 add apkfrombundle.py 2023-01-24 22:08:20 +04:00
luvletter2333 5890d96dda
ci: use script to publish release and CI builds 2023-01-22 15:56:32 +08:00
luvletter2333 c71626f80e
fix: use latest photo editor 2023-01-21 20:56:50 +08:00
luvletter2333 adcafbfc6d
feat: option to use telegram official translateAlert in chat 2023-01-21 19:03:45 +08:00
luvletter2333 12bbf436a1
fix: edit proxy info 2023-01-21 18:16:49 +08:00
luvletter2333 7ef3a53a2f
fix: missing qr code scan in Dialogs 2023-01-21 18:09:59 +08:00
luvletter2333 5aa483a795
fix merge: v9.3.3 2023-01-21 16:59:07 +08:00
James Clef 8f4e6d9428
fix merge: v9.3.3 2023-01-21 16:52:50 +08:00
luvletter2333 69c6fce26b
merge official 9.3.3 2023-01-21 16:25:21 +08:00
luvletter2333 27ced58f8c
fix: disable mapDrifting when useOSM in NekoGeneral 2023-01-21 14:34:20 +08:00
luvletter2333 f11f97bd2e
fix: NPE about currentBackgroundDrawable 2023-01-21 14:34:16 +08:00
luvletter2333 2eb50eca97
fix CI gradle 2023-01-21 14:24:29 +08:00
luvletter2333 2e15f20707
update translation 2023-01-21 14:15:25 +08:00
luvletter2333 92d0a8e072
bump version to 9.1.6 2023-01-21 14:13:31 +08:00
luvletter2333 8673b032af
fix more button in ProfileActivity 2023-01-21 14:01:58 +08:00
luvletter2333 85688e84af
remove google libs from noGcm 2023-01-21 13:01:52 +08:00
luvletter2333 355df746a1
fix osmdroid map loading 2023-01-17 00:41:25 +08:00
xaxtix 1c03d75ea8 update to 9.3.3 2023-01-04 03:17:33 +04:00
xaxtix 5a31f93ec2 update to 9.3.2 2022-12-31 14:47:53 +04:00
xaxtix 3ccf875b1b update to 9.3.0 2022-12-30 16:32:20 +04:00
xaxtix 2c71f6c92b update to 9.2.2 2022-12-09 11:34:00 +04:00
xaxtix 03e899e4ec update to 9.2.1 2022-12-07 19:13:09 +04:00
xaxtix b73fc8de38 update to 9.2.0 2022-12-06 23:13:44 +04:00
luvletter2333 2077f74a85
impl OSMDroid Map (not finished) 2022-12-03 18:12:41 +08:00
luvletter2333 89dee06649
fix possible crash in PhotoPicker 2022-12-03 14:39:39 +08:00
luvletter2333 17546df4bb
try to downgrade official version code and bump version 2022-12-03 14:37:59 +08:00
luvletter2333 9d4ce33277
fix leakage of ZipUtil, add back Icon in VoIP 2022-12-03 02:38:42 +08:00
luvletter2333 07b9af621d
revert BottomSheet changes 2022-12-03 02:21:21 +08:00
luvletter2333 2984ee4ef3
fix NPE about LinearLayout 2022-12-03 02:03:38 +08:00
luvletter2333 f56b39d56d
fix CI builds 2022-12-03 02:03:28 +08:00
luvletter2333 8c043db2ce
fix NPE in ChatMessageCell 2022-12-02 18:01:53 +08:00
luvletter2333 85ff913c2b
enable universal abi for CI builds 2022-12-02 18:01:53 +08:00
luvletter2333 9ed10404e5
fix merge 9.1.6 2022-12-02 17:17:53 +08:00
luvletter2333 b95fa5971d
merge official 9.1.6 2022-12-02 16:57:16 +08:00
luvletter2333 6b59317fa6
merge official 2022-12-02 16:15:42 +08:00
xaxtix 0e17caa7a6 update to 9.1.6 2022-11-24 16:30:01 +04:00
xaxtix 97ab6c63c8 update to 9.1.5 2022-11-21 20:49:32 +04:00
xaxtix 9bd017a72a update to 9.1.4 2022-11-21 01:12:39 +04:00
xaxtix 4951061dde update to 9.1.3 2022-11-12 12:41:35 +04:00
luvletter2333 25a1933416
revert legacy external storage 2022-11-07 11:49:02 +08:00
xaxtix 23118a4a6c update to 9.1.2 2022-11-07 00:55:13 +04:00
xaxtix a2981ddd29 update to 9.1.1 2022-11-06 06:42:06 +04:00
xaxtix 342e681114 update to 9.1.1 2022-11-06 05:16:30 +04:00
xaxtix 67b1fd17e6 update to 9.1.0 2022-11-05 16:34:47 +04:00
luvletter2333 079b3bb355
fix crash at premium subscription 2022-11-05 04:12:38 +08:00
luvletter2333 46f7f843b1
rewrite ws 2022-11-03 22:00:59 +08:00
luvletter2333 0b68460d21
try fix WsProxy 2022-11-03 19:41:39 +08:00
luvletter2333 50f2b934c5
fix WRITE_EXTERNAL_STORAGE in scoped storage & remove LegacyExternalStorage 2022-11-03 16:51:26 +08:00
luvletter2333 a5393e9e9d
defer KeepAliveJob? 2022-11-02 03:52:53 +08:00
luvletter2333 68708c8ff3
fix PendingIntent in Android 12 2022-11-02 03:38:49 +08:00
luvletter2333 de75de1f2a
fix emoji 2022-11-01 17:52:52 +08:00
luvletter2333 a4aa5f8099
upgrade dependencies 2022-11-01 17:52:52 +08:00
luvletter2333 9af95477cc
revert to baseline icons 2022-11-01 15:00:24 +08:00
luvletter2333 5e4e11dbcd
fix appname 2022-10-16 00:28:07 +08:00
luvletter2333 905cf0a7b3
merge official cont, remove google parts 2022-10-11 11:44:37 +08:00
luvletter2333 4c8b0b14fa
merge official 2022-10-11 11:07:58 +08:00
xaxtix e9a35cea54 update to 9.0.2 2022-09-21 18:20:30 +04:00
xaxtix 11edd5ee0d update to 9.0.1 2022-09-18 21:02:37 +04:00
xaxtix abb896635f update to 9.0.0 2022-09-16 22:48:21 +04:00
rhuba8324 086b1a6b1c
Update zh-tw translation (#903) 2022-08-30 23:49:18 +08:00
Hykilpikonna 2340eea7e9
readme: Categorize readme feature list (#855) 2022-08-30 23:48:42 +08:00
Furkan Karcıoğlu e653a90ebd
Translation Updates from Weblate 2022-08-30 23:47:14 +08:00
luvletter2333 2b9cdaab4f
fix: BackButton color in ChatActivity 2022-08-30 23:42:52 +08:00
luvletter2333 ed3ba3dcb5
fix: zipFile leak in FileUtil 2022-08-30 23:12:55 +08:00
luvletter2333 0aaaa283cf
debug: track application locale change 2022-08-30 20:20:19 +08:00
luvletter2333 e59512fa7c
fix OSM overlay in gmaps 2022-08-30 19:10:30 +08:00
luvletter2333 6bf77388d7
fix GoogleMaps Drifting, Add Map sections in NekoConfig/General 2022-08-30 18:36:29 +08:00
luvletter2333 92f75541ec
fix: double share 2022-08-30 17:50:09 +08:00
luvletter2333 4f17a21015
update twitter emoji 2022-08-30 17:09:29 +08:00
luvletter2333 70bb838901
fix emojiView style 2022-08-30 15:58:11 +08:00
luvletter2333 a7c7030271
Revert "fix: dont reload proxylist in ConnectionsManager's init"
This reverts commit 41a47438
2022-08-30 15:50:18 +08:00
luvletter2333 4ca3b95c0c
bump to java 11, remove Android 4.1 support 2022-08-18 02:01:37 +08:00
luvletter2333 7d2d1d12aa
Fix Google Maps, add API Key 2022-08-18 01:56:57 +08:00
luvletter2333 bd9483a80f
Fix ChatEnterView icons and InputMenu 2022-08-17 23:42:53 +08:00
luvletter2333 575fd5a684
Fix ChatEnterView baseline icon 2022-08-17 22:15:03 +08:00
luvletter2333 7d1f137c21
Fix merge 8.9.3
Move GooglePushListenerServiceProvider into gservices/
Remove Premium Gifting
Optimize ApplicationLoader init

TODO:
1. fix ChatMenu
2. fix MapsProvider and LocationProvider
2022-08-17 21:27:36 +08:00
luvletter2333 db06bb66f4
fix google-services.json path 2022-08-17 17:26:39 +08:00
luvletter2333 8b2dca3ea7
upgrade dependencies & minSdk 19 2022-08-17 17:09:53 +08:00
luvletter2333 6c5fae9fae
merge official 8.9.3
TODO:
1. Fix FCM
2. Fix OSM
3. Add Google Maps into release
2022-08-17 16:45:51 +08:00
xaxtix 43fe75b46b update to 8.9.3 2022-08-14 22:44:20 +04:00
xaxtix 172335d189 update to 8.9.2 2022-08-13 21:17:20 +04:00
xaxtix b1aa2020e5 update to 8.9.0 2022-08-12 19:23:51 +04:00
luvletter2333 41a4743870
fix: dont reload proxylist in ConnectionsManager's init 2022-08-04 00:37:18 +08:00
luvletter2333 bfb3168c36
test: add enhanced download speed 2022-08-04 00:23:36 +08:00
luvletter2333 dcff475b34
fix ChatEditActivity icon 2022-07-27 21:23:37 +08:00
luvletter2333 68ae4ede1f
try fix admin count 2022-07-27 21:23:37 +08:00
luvletter2333 6f1f65d1ac
feat: recent accessed chats 2022-07-27 21:11:23 +08:00
luvletter2333 bca854c8af
debug: getThemedDrawable failed for currentBackgroundDrawable 2022-07-27 18:18:52 +08:00
luvletter2333 0a5c733149
refine Internal Updater 2022-07-27 17:54:09 +08:00
luvletter2333 0462fe9875
update translation 2022-07-27 17:35:50 +08:00
luvletter2333 e70805c398
fix: sticker: send wo sound 2022-07-27 17:32:22 +08:00
luvletter2333 e879e46f09
bump version 2022-07-27 17:23:45 +08:00
luvletter2333 8f18d21c78
i18n: add translation for zh-rCN 2022-07-27 16:39:49 +08:00
luvletter2333 4151c906a2
fix: Add avatar when no avatar 2022-07-27 16:05:47 +08:00
Next Alone bcec381654
fix: output devices 2022-07-27 15:22:42 +08:00
luvletter2333 6ccb87a96e
fix: read channel admin count 2022-07-27 14:41:49 +08:00
Next Alone fd452a03ac
fix: bottom sheet when long press link in profile 2022-07-27 14:14:42 +08:00
luvletter2333 b87cd3624d
ci: fix send build 2022-07-27 14:10:51 +08:00
luvletter2333 93a286ba44
try fix NPE in ChatMessageCell 2022-07-26 12:04:19 +08:00
luvletter2333 383be2281f
defer NotificationCenter error level log 2022-07-24 14:28:48 +08:00
luvletter2333 271b0dc136
fix LoginActivity NPE 2022-07-13 16:47:45 +08:00
luvletter2333 66eb744fc7
fix save to gallery 2022-07-13 00:50:00 +08:00
luvletter2333 3c1d1bf7cd
bump version 2022-07-13 00:38:24 +08:00
luvletter2333 27cb3dc403
refactor NekoSettings 2022-07-13 00:23:51 +08:00
luvletter2333 db86538019
add disablePremiumStickerAnimation 2022-07-13 00:23:51 +08:00
luvletter2333 ef9e0aa3d5
update translation 2022-07-13 00:23:50 +08:00
luvletter2333 36ce0178f6
try fix CI bot api 2022-07-13 00:23:45 +08:00
luvletter2333 c00cac6b28
fix ChatEditActivity shadow 2022-07-12 22:33:49 +08:00
luvletter2333 de6d5e01f3
fix emoji 2022-07-12 22:33:30 +08:00
luvletter2333 946f24c45b
fix links BottomSheet 2022-07-12 22:33:19 +08:00
luvletter2333 31abbbbddd
revert back behavior 2022-07-12 22:29:22 +08:00
luvletter2333 8f4741f8ab
fix HeaderCell 2022-07-12 21:09:32 +08:00
luvletter2333 e8accc449d
try fix storage path width 2022-07-10 16:04:21 +08:00
luvletter2333 c71f3240a3
try fix third-party cache path 2022-07-10 15:26:20 +08:00
luvletter2333 b2a57d7c02
bump version 2022-07-10 15:11:33 +08:00
luvletter2333 ec0caec469
try fix tabs crash 2022-07-10 15:10:49 +08:00
luvletter2333 f8a72d335e
fix ChatEditActivity with no admin rights 2022-07-10 15:10:36 +08:00
luvletter2333 11f055d23f
fix neko Icon selector 2022-07-10 14:58:02 +08:00
luvletter2333 de2d16705a
fix neko dialog filter 2022-07-10 14:03:25 +08:00
xtaodada a3e489c2f9
fix: Profile parse links 2022-07-10 13:51:43 +08:00
xtaodada 7595ba071e
fix: useSystemEmoji 2022-07-10 13:51:31 +08:00
luvletter2333 7dcb3abadc
fix unlimited accounts 2022-07-08 10:56:41 +08:00
luvletter2333 f002ddc261
bump version 2022-07-07 17:27:32 +08:00
luvletter2333 d7e8ce80e3
fix NPE in HideAllTabs 2022-07-07 17:23:32 +08:00
luvletter2333 6e17a698e0
revert 882 sticker tab height 2022-07-07 16:45:49 +08:00
luvletter2333 ac8950ad0f
fix HeaderCell orientation 2022-07-07 16:13:37 +08:00
luvletter2333 8e25de7902
fix hide all chats 2022-07-07 15:13:46 +08:00
luvletter2333 c15d817e81
fix logCell in ChatEditActivity 2022-07-07 14:08:18 +08:00
Next Alone 8f3d631d4f
fix: add account 2022-07-07 14:03:51 +08:00
Next Alone 5f9c66e5be
fix: large avatar in drawer 2022-07-07 14:03:46 +08:00
Next Alone eab8088cae
fix: hide all chat 2022-07-07 14:03:07 +08:00
luvletter2333 d0e1ebd029
remove auto delete timer, #854 2022-07-06 20:48:53 +08:00
luvletter2333 77d0e82cb0
Fix icon padding #611 2022-07-06 20:42:29 +08:00
luvletter2333 a0f3ad497d
merge official 8.8.5 2022-07-06 20:37:05 +08:00
luvletter2333 4f85d02518
fix merge 8.8.3 2022-07-06 20:33:37 +08:00
luvletter2333 531f4f9ba9
fix merge 8.8.3 2022-07-06 20:07:52 +08:00
xaxtix 6cb1cdf898 update to 8.8.5 2022-07-04 15:54:30 +04:00
luvletter2333 0a05e0476b
Merge official v8.8.4 2022-06-30 19:19:47 +08:00
luvletter2333 73cb11f883
Merge official v8.8.3 2022-06-30 19:16:32 +08:00
xaxtix 43401a515c update to 8.8.4 2022-06-28 14:33:06 +04:00
xaxtix 0a3abc5158 update to 8.8.4 2022-06-28 14:00:33 +04:00
luvletter2333 2a317b62ac
keep downloaded files' origin name 2022-06-25 20:57:52 +08:00
luvletter2333 8057580313
bump version 2022-06-25 20:48:50 +08:00
luvletter2333 018c93db24
fix forward preview text 2022-06-25 20:47:02 +08:00
luvletter2333 1c3495c92e
fix user status at first enter 2022-06-25 20:39:52 +08:00
luvletter2333 780dc4d5b6
delay proxy update 2022-06-25 20:16:51 +08:00
luvletter2333 00f2a0271c
fix proxy flush and enabled 2022-06-25 20:08:59 +08:00
xaxtix 4a95c2fc1f Update to 8.8.3 2022-06-23 02:07:45 +04:00
luvletter2333 47ba651ce1
update dependencies 2022-06-22 00:55:12 +08:00
luvletter2333 58d91d99aa
refactor ws relay 2022-06-22 00:54:05 +08:00
luvletter2333 e44072e04b
fix view in chat & search 2022-06-21 21:45:48 +08:00
luvletter2333 013a7aaa89
fix CI: install ndk manually 2022-06-21 21:02:33 +08:00
luvletter2333 1cf9174b75
revert forward name 2022-06-21 20:34:09 +08:00
luvletter2333 23952a1dd5
fix view in chat clickable 2022-06-21 18:11:39 +08:00
xaxtix 96dce2c9aa Update to 8.8.2 2022-06-21 06:51:00 +04:00
luvletter2333 e3fcc75eca
fix splash icon 2022-05-08 16:50:05 +08:00
luvletter2333 21563770fc
allow test backend 2022-05-08 15:31:39 +08:00
luvletter2333 067deb365d
allow details 2022-05-08 15:17:04 +08:00
luvletter2333 06df7442ef
update translation 2022-05-08 15:10:36 +08:00
hhn1611245010 6aa94cff52
fix issue #758 (#822) 2022-05-08 15:02:10 +08:00
luvletter2333 37c2793e69
Merge official 8.7.4 2022-05-08 15:01:14 +08:00
luvletter2333 fa99e892bc
Merge official 8.7.0 2022-05-08 14:59:55 +08:00
xaxtix 32aef72421 Update to 8.7.4 2022-04-28 23:24:40 +03:00
xaxtix 136c9582a8 Update to 8.7.3 2022-04-28 17:17:48 +03:00
xaxtix 8283c12364 Update to 8.7.2 2022-04-23 12:51:09 +03:00
xaxtix 8309ac8d7c Update to 8.7.1 2022-04-21 21:03:20 +03:00
luvletter2333 10016b3e49
add translation 2022-04-19 20:05:59 +08:00
xaxtix 1e50785b90 Update to 8.7.0 2022-04-16 17:43:17 +03:00
luvletter2333 b16a3bb372
remove unused proxy changes 2022-04-12 20:28:29 +08:00
Weblate (bot) 0b83d38bba
Translations update from Hosted Weblate 2022-04-12 16:27:32 +08:00
luvletter2333 a11fc83509
bump versioncode 2022-04-12 16:12:12 +08:00
luvletter2333 9115bb9569
Revert "Update translation"
This reverts commit 8d5ef289
2022-04-12 15:52:02 +08:00
ChristinaLee 51a3dad697
implement issue #614 (#806)
allow copy link in private message
2022-04-12 15:20:12 +08:00
luvletter2333 9023f54a10
bump version 2022-04-12 14:35:28 +08:00
luvletter2333 8d5ef289a2
Update translation 2022-04-12 14:27:56 +08:00
luvletter2333 324c39dded
Fix stickerset drag 2022-04-12 14:19:54 +08:00
gao_cai_sheng b709d28bff
ci: disable upload to Telegram for pull request (#808) 2022-04-05 13:17:20 +08:00
NatsumeMio 88ef26f33a
Skip to the latest message when double tap under Tablet mode (#749) (#798) 2022-03-29 15:34:28 +08:00
NatsumeMio 9e7fd90610
Use photo editor after file picker (#797)
* open editor for files from file picker
2022-03-29 15:32:50 +08:00
NatsumeMio 8f54fb267d
Fix video path (#796)
* Fix video path if can't read directly
2022-03-28 21:28:51 +08:00
NatsumeMio 1c123dedc6
add direct share for url (#768) 2022-03-28 17:42:54 +08:00
luvletter2333 55024ce8bb
try to fix persion date 2022-03-27 22:22:33 +08:00
Legend 0aabf18b0c
WS Relay payload type fix (#781)
Payload should be Base64 string, not a number.
2022-03-27 22:22:33 +08:00
luvletter2333 9969037be2
try to fix status bar 2022-03-27 22:22:33 +08:00
luvletter2333 ff45bdac1e
update translation 2022-03-27 16:06:44 +08:00
luvletter2333 79c6d00fbb
revert media encoding parameters 2022-03-24 20:31:34 +08:00
luvletter2333 538e82ff01
improve accessibility 2022-03-24 14:45:35 +08:00
luvletter2333 0ed8ff1ed3
bump version to 8.6.2-rc01 2022-03-24 14:33:01 +08:00
luvletter2333 00f7b2c892
fix delete all 2022-03-24 14:28:47 +08:00
luvletter2333 2f86e87899
update translation 2022-03-24 14:14:45 +08:00
luvletter2333 a6c39637da
merge official 8.6.2 2022-03-24 13:58:27 +08:00
xaxtix 0abe4541dd Update to 8.6.2 2022-03-20 23:22:37 +03:00
luvletter2333 1b78fc6cf1
bump version 2022-03-15 20:21:10 +08:00
luvletter2333 8cf8776235
fix botlogin UI 2022-03-15 20:18:20 +08:00
luvletter2333 4b010e3b11
fix qrcode scanner 2022-03-15 20:03:48 +08:00
luvletter2333 c1a21028e4
allow force enabling chat blur 2022-03-15 17:22:27 +08:00
luvletter2333 de17e63f2a
allow adding to tabs when searching 2022-03-15 15:43:31 +08:00
luvletter2333 7a29fe9d2d
Fix download manager 2022-03-15 15:33:03 +08:00
luvletter2333 8de1a84dc0
add back NekoX functions to LoginActivity 2022-03-14 02:19:53 +08:00
luvletter2333 c26b65bee9
fix CI: ffmpeg build 2022-03-13 23:28:54 +08:00
luvletter2333 8311829a9b
fix updater npe 2022-03-13 22:57:25 +08:00
luvletter2333 fa5b44b51e
merge official v8.6.1 2022-03-13 22:48:25 +08:00
luvletter2333 3b8cf4fde4
fix download icon 2022-03-13 19:33:21 +08:00
xaxtix 5d5527525f Update to 8.6.1 2022-03-13 04:58:00 +03:00
xaxtix d30f796d8c Update to 8.6.0 2022-03-11 19:49:54 +03:00
luvletter2333 86c7a6d0be
update translation 2022-03-07 16:47:40 +08:00
luvletter2333 7e03bca531
fix open link request 2022-03-07 16:42:43 +08:00
luvletter2333 4a1e66b96b
update Compilation Guide & add yasm checker 2022-02-28 21:40:41 +08:00
luvletter2333 d3511e568b
retry to send apks 2022-02-28 19:56:14 +08:00
luvletter2333 6d4e8a06c8
bump version 2022-02-28 19:50:41 +08:00
luvletter2333 be3ff350fb
show full url 2022-02-28 19:42:07 +08:00
luvletter2333 1c57ed952e
fixes 2022-02-28 16:00:41 +08:00
luvletter2333 9fd741ffa0
allow save video sticker 2022-02-28 15:12:37 +08:00
luvletter2333 959f9dfc26
fix fwd in Replies 2022-02-28 14:08:05 +08:00
luvletter2333 8a999c6aaf
update translation 2022-02-28 13:49:56 +08:00
luvletter2333 b10b2e0f7c
Update readme 2022-02-20 19:41:56 +08:00
luvletter2333 34d78450c7
Fix long userId? 2022-02-20 19:15:37 +08:00
luvletter2333 f406f07718
Change verName to 8.5.4-rc01 2022-02-18 17:33:24 +08:00
luvletter2333 1bece71050
Improve longPress Repeat 2022-02-18 17:32:36 +08:00
luvletter2333 c19229663f
Fix Attach missing when using input menu 2022-02-18 16:45:27 +08:00
luvletter2333 ab0c0ed4d8
Fix possible native crash? 2022-02-18 16:01:55 +08:00
luvletter2333 6512972f30
Allow to toggle noquote when forwarding to multiple targets 2022-02-18 15:50:18 +08:00
luvletter2333 f438230f13
Fix icon 2022-02-18 15:25:38 +08:00
luvletter2333 32066ecf1f
Merge official 8.5.4 2022-02-18 15:23:22 +08:00
xaxtix 3708e9847a Update to 8.5.4 2022-02-16 03:22:36 +03:00
xaxtix b5694a1ca6 Update to 8.5.3 2022-02-15 23:18:49 +03:00
luvletter2333 0379caa36f
Update translation 2022-02-12 15:13:52 +08:00
luvletter2333 92bb673a32
Merge official 8.5.2 2022-02-12 15:12:39 +08:00
xaxtix 3b4919a542 Update to 8.5.1 2022-02-12 08:22:45 +03:00
luvletter2333 8a3dc467d3
Update translation 2022-02-06 23:24:59 +08:00
luvletter2333 016bdc4a7d
Bump version 2022-02-06 23:21:51 +08:00
luvletter2333 d01678de23
Fix double translate 2022-02-06 23:21:51 +08:00
luvletter2333 47724d4d61
Fix test dc 2022-02-06 23:21:51 +08:00
luvletter2333 ba9f84efee Fix release CI 2022-02-04 22:09:14 +08:00
luvletter2333 62c7c8b5cf Replace icons 2022-02-04 21:21:53 +08:00
luvletter2333 c811f7d06b Fix ffmpeg build 2022-02-04 20:17:14 +08:00
luvletter2333 6ca737b4e1 Fix ProfileActivity Bio 2022-02-04 17:54:31 +08:00
luvletter2333 672c7e8688 Fix libvpx x86 build 2022-02-04 15:44:02 +08:00
luvletter2333 8d07f4d1ad Update CI native 2022-02-04 15:44:02 +08:00
luvletter2333 c706d2c2c5 Bump version 2022-02-04 11:46:17 +08:00
luvletter2333 be79f8f6de Merge official v8.5.1 2022-02-04 11:38:57 +08:00
luvletter2333 c60fd69b04 Fix ffmpeg build script 2022-02-04 11:28:29 +08:00
xaxtix 146cea26cd Update to 8.5.1 2022-02-04 01:18:58 +03:00
luvletter2333 db1ba95ae5 Fix libvpx builds 2022-02-04 00:45:34 +08:00
luvletter2333 b1d798c8d3 Fix merge 2022-02-04 00:45:34 +08:00
luvletter2333 83e8a1b61a Update libvpx build 2022-02-03 23:27:34 +08:00
luvletter2333 d4e8d513c4 Update native build scripts from tfoss 2022-02-03 23:25:39 +08:00
luvletter2333 cdc56f97e7 Update ffmpeg to v4.3.3 2022-02-03 23:17:38 +08:00
luvletter2333 d54dd02b59 Merge official v8.5.0 2022-02-03 23:02:55 +08:00
luvletter2333 e36c89be5a Update translation 2022-02-03 22:21:33 +08:00
gao_cai_sheng fd0ea5b158
feat: add setAliasName (#739)
* feat: add setAliasName

Signed-off-by: qwq233 <qwq233@qwq2333.top>
2022-02-03 22:18:54 +08:00
luvletter2333 00ae420abf allow show bottom actions 2022-02-03 21:25:21 +08:00
luvletter2333 20f3658279 Add back asking for gallery permission 2022-02-03 20:44:42 +08:00
luvletter2333 2a19e63c62 Remove icon in ChatEditActivity and fix permission count 2022-02-03 19:30:00 +08:00
xaxtix a1817ea7d3 Update build_ffmpeg script 2022-02-01 19:07:33 +03:00
xaxtix ee217d739f Update to 8.5.0 2022-02-01 17:05:27 +03:00
xaxtix ad05eea682 Update to 8.5.0 2022-02-01 16:00:45 +03:00
luvletter2333 040500f1d9 Fix updater apk matcher 2022-01-27 21:14:25 +08:00
luvletter2333 3eaa650947 Bump version 2022-01-27 21:03:00 +08:00
luvletter2333 ac308681c6 Fix string 2022-01-27 21:02:06 +08:00
luvletter2333 fef56f8a49 Send commit message in debug build 2022-01-27 21:02:06 +08:00
luvletter2333 d041495b30
Update translation 2022-01-27 19:52:43 +08:00
luvletter2333 2c4fdbc001
Update translation 2022-01-23 17:41:53 +08:00
luvletter2333 91857d5a37
Fix 2FA password reset 2022-01-23 17:39:03 +08:00
luvletter2333 a6457cac77
Bump version to v8.4.4-rc02 2022-01-23 15:50:41 +08:00
luvletter2333 7aab8fad6d
Fix release actions 2022-01-23 15:49:40 +08:00
luvletter2333 d3ff18c668
Fix updater 2022-01-23 15:49:24 +08:00
luvletter2333 d67c5c27b9
Refactor Updater 2022-01-23 14:19:32 +08:00
luvletter2333 76ef6ac602
Update translations 2022-01-21 17:23:35 +08:00
luvletter2333 7a63017f54
Fix debug build verName 2022-01-21 17:19:22 +08:00
luvletter2333 1d3011ea58
Feature: custom save path 2022-01-21 17:17:27 +08:00
luvletter2333 b848e5e666
Add version name to apk filename 2022-01-21 16:53:16 +08:00
luvletter2333 9861e319f4
Remove unused optimizer 2022-01-21 15:25:46 +08:00
luvletter2333 8618ac484b
Feature: allow hiding reactions in message menu, or displaying when double tap 2022-01-21 15:24:30 +08:00
luvletter2333 e8e446f586
Set probability to show update channel 2022-01-20 23:27:58 +08:00
luvletter2333 632b36170c
Feature: disable all notification bubbles 2022-01-20 23:07:24 +08:00
luvletter2333 cf712d9641
Fix compile script 2022-01-20 23:06:38 +08:00
luvletter2333 200a359dd7
Change version 8.4.4-rc01 2022-01-20 16:11:09 +08:00
luvletter2333 f64c20581d
Fix crash when mention 2022-01-20 16:05:47 +08:00
luvletter2333 b1dce3510f
Remove customApi and Disable dev mode for users 2022-01-20 15:41:31 +08:00
luvletter2333 e1c74d2aaf
Merge official 8.4.4 2022-01-20 15:26:38 +08:00
luvletter2333 2456ab21f1
Merge remote-tracking branch 'weblate/main' 2022-01-20 15:15:12 +08:00
luvletter2333 c4afcbf6f2
Update translation 2022-01-20 15:07:24 +08:00
luvletter2333 6a69aa0e9c
Fix display spoiler 2022-01-20 14:51:17 +08:00
luvletter2333 e46ea1135e
Fix string 2022-01-20 14:44:46 +08:00
6666666 91a4b200dd
Translated using Weblate (Chinese (Simplified))
Currently translated at 99.2% (124 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/zh_Hans/
2022-01-20 02:54:20 +01:00
xaxtix f0f858ad3f Update to 8.4.4 2022-01-18 23:51:58 +03:00
luvletter2333 56a79e2f35
feat: disable double tab reactions 2022-01-17 18:57:31 +08:00
Radshasak Paitoonlerd 3f410a2017
Translated using Weblate (Thai)
Currently translated at 13.6% (36 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/th/
2022-01-17 11:56:44 +01:00
luvletter2333 2de5fba21f
feat: display spoiler messages directly 2022-01-17 17:57:56 +08:00
Radshasak Paitoonlerd 66ce1f0fb7
Added translation using Weblate (Thai) 2022-01-17 10:24:13 +01:00
luvletter2333 2a6708a4cc
Refactor nekogram package structure 2022-01-17 16:53:22 +08:00
luvletter2333 2237ece95d
Refactor proxies structure 2022-01-17 15:39:45 +08:00
luvletter2333 130230f081
Fix release CI 2022-01-17 15:18:39 +08:00
luvletter2333 f4d4851672
Bump version 2022-01-17 15:07:31 +08:00
luvletter2333 47d3372766
Update translation 2022-01-16 16:23:30 +08:00
luvletter2333 2288606583
try to fix CI? 2022-01-16 15:58:57 +08:00
luvletter2333 f973694b41
Refine save to download 2022-01-16 15:21:09 +08:00
luvletter2333 6249aba960
Refactor configs 2022-01-16 15:00:58 +08:00
luvletter2333 e78748b6b8
Merge official 8.4.3 2022-01-16 14:47:30 +08:00
Denys Nykula 865bf40424
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (125 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/uk/
2022-01-14 15:54:02 +01:00
Denys Nykula 270143347d
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (263 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/uk/
2022-01-14 15:54:01 +01:00
Denys Nykula a0f23bad7d
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (125 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/uk/
2022-01-13 20:17:09 +01:00
Denys Nykula a21fbf2dc6
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (263 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/uk/
2022-01-13 20:17:09 +01:00
FW 0e7240ed97
Translated using Weblate (German)
Currently translated at 100.0% (125 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/de/
2022-01-13 01:51:29 +01:00
araccaine 0aeec8b717
Translated using Weblate (German)
Currently translated at 100.0% (263 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/de/
2022-01-13 01:51:29 +01:00
FW d7c7e5d63c
Translated using Weblate (German)
Currently translated at 100.0% (263 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/de/
2022-01-13 01:51:28 +01:00
bruh ff11feb1a6
Translated using Weblate (Vietnamese)
Currently translated at 100.0% (125 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/vi/
2022-01-11 14:55:26 +01:00
qqqq1 896904b499
Translated using Weblate (Persian)
Currently translated at 100.0% (125 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/fa/
2022-01-11 04:54:46 +01:00
qqqq1 4cb2b02b4d
Translated using Weblate (Persian)
Currently translated at 100.0% (263 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/fa/
2022-01-11 04:54:46 +01:00
qqqq1 e44ac90834
Translated using Weblate (Persian)
Currently translated at 95.8% (252 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/fa/
2022-01-11 02:56:44 +01:00
xaxtix ad6629f6fc Update to 8.4.3 2022-01-10 05:27:47 +03:00
Sam 86dd5c2a6e
Translated using Weblate (Chinese (Traditional, Hong Kong))
Currently translated at 74.1% (195 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/zh_Hant_HK/
2022-01-07 08:56:10 +01:00
Traditional Chinese a3ab5bfa3c
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (125 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/zh_Hant/
2022-01-03 16:53:01 +01:00
Traditional Chinese 9df9a377bf
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (263 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/zh_Hant/
2022-01-03 16:53:01 +01:00
Traditional Chinese befd389536
Translated using Weblate (Chinese (Traditional, Hong Kong))
Currently translated at 73.7% (194 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/zh_Hant_HK/
2022-01-03 16:53:00 +01:00
ssantos 26f90bdb61
Translated using Weblate (Portuguese)
Currently translated at 100.0% (263 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/pt/
2022-01-02 18:54:27 +01:00
Reza Esmaeili a8914934c7
Translated using Weblate (Persian)
Currently translated at 96.0% (120 of 125 strings)

Translation: NekoX/NekoX ( 2 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox-1/fa/
2022-01-02 11:52:51 +01:00
Reza Esmaeili c36ac8299e
Translated using Weblate (Persian)
Currently translated at 95.0% (250 of 263 strings)

Translation: NekoX/NekoX ( 1 / 2 )
Translate-URL: https://hosted.weblate.org/projects/nekox/nekox/fa/
2022-01-02 11:52:51 +01:00
luvletter2333 a413352c45
Bump version 2022-01-02 00:01:57 +08:00
luvletter2333 b472662a3f
Use self-hosted bot api server to upload 2022-01-01 23:42:47 +08:00
luvletter2333 097acd41c7
update translation 2022-01-01 21:47:32 +08:00
luvletter2333 50ed8cfb8b
Fix toggle draw snow 2022-01-01 21:41:02 +08:00
luvletter2333 305c7fe18d
Kill official translator 2022-01-01 20:04:00 +08:00
luvletter2333 c39ab4ce95
Merge Dev 2022-01-01 19:51:13 +08:00
luvletter2333 785a883f4e
Move QRCode into bottom builder 2022-01-01 19:48:02 +08:00
luvletter2333 0c53f2b9ed
Kill QRCode animation 2022-01-01 02:29:48 +08:00
luvletter2333 9870b8e343
Try to fix freeze in LanguageSelectActivity 2022-01-01 02:27:29 +08:00
luvletter2333 7474cd9718
Merge official 8.4.1 & 8.4.2 2022-01-01 02:10:02 +08:00
xaxtix c1c2ebaf46 Update to 8.4.2 2021-12-30 21:54:06 +03:00
xaxtix 9e740dfd4d Update to 8.4.1 2021-12-30 13:52:40 +03:00
Luv Letter 1b5efe7178
Bump version for play build 2021-12-29 12:01:34 +08:00
luvletter2333 d0bf834786
Fix delete all 2021-12-27 19:07:38 +08:00
luvletter2333 2584e307a6
Fix CI: skip libvpx when cache hit 2021-12-27 17:57:49 +08:00
luvletter2333 fdb6ee227a
Update translation 2021-12-27 17:54:19 +08:00
luvletter2333 4c8c6de621
Fix delete & ban channel 2021-12-27 17:52:10 +08:00
luvletter2333 21e8c8432e
Fix Release CI 2021-12-27 14:26:22 +08:00
Luv Letter 3cf4eb12f7
Bump version to v8.3.1 2021-12-27 11:46:36 +08:00
Luv Letter 4e7e596959
Fix wrong delete all 2021-12-25 02:14:12 +08:00
luvletter2333 e4924c245a
Add libvpx build for fdroid 2021-12-24 01:07:59 +08:00
luvletter2333 ecb6ec1c8e
Fix CI 2021-12-24 00:36:14 +08:00
luvletter2333 65f7479c7c
Add libvpx build for x86 and x86_64 2021-12-24 00:17:48 +08:00
thermatk ef1107f2f5
[TF][JNI] libvpx rebuild binaries
(cherry picked from commit 699e7b41bf7d99a01d864bde8912ef8164c5d142)
2021-12-23 23:52:51 +08:00
thermatk 0bed4a37d0
[TF][JNI] kill libvpx binaries
(cherry picked from commit 00f05c8b171f1ea23687bb502238158eac3f39fe)
2021-12-23 23:51:12 +08:00
luvletter2333 6f01267133
Update boringssl 2021-12-23 23:48:29 +08:00
luvletter2333 be93f69a31
Use popup in NekoSettings 2021-12-23 23:24:49 +08:00
luvletter2333 67703b1717
Remove accept secret chat 2021-12-23 23:06:53 +08:00
luvletter2333 5f41f3f0cb
Update translation 2021-12-23 23:00:42 +08:00
luvletter2333 cde8d42dc7
Add hide send as channel 2021-12-23 22:58:13 +08:00
luvletter2333 bb2acb6a29
Fix showMessageSeen, not larger than 2021-12-23 22:40:53 +08:00
luvletter2333 6e60b3ab64
Bump version to v8.3.0 2021-12-21 16:16:15 +08:00
luvletter2333 6666c0d566
update translation 2021-12-21 16:13:19 +08:00
luvletter2333 99d38cb371
CI: send debug builds to devs 2021-12-21 15:47:15 +08:00
luvletter2333 72cb3361e1
Remove useless sdk fix 2021-12-21 14:29:12 +08:00
luvletter2333 eacd2894ca
Fix emoji theme always loading 2021-12-21 14:05:37 +08:00
luvletter2333 1d46cd930f
Fix diff with official 2021-12-21 14:05:11 +08:00
luvletter2333 23f7d3f443
Fix TLRPC files diff with official 2021-12-21 00:56:41 +08:00
luvletter2333 7c5a12e340
Fix Theme files diff with official 2021-12-21 00:52:52 +08:00
luvletter2333 663dc36f12
Update dependencies 2021-12-20 23:57:51 +08:00
luvletter2333 bc309b2104
Refine fdroid build, enable universalApk 2021-12-20 23:20:25 +08:00
Алексей Муратов c25a49894b
Share a message in another app (#693)
* add nkbtn_sharemessage
2021-12-16 16:23:20 +08:00
Алексей Муратов d9c968cbf4
ShareMyKey in private chats (#685) 2021-12-16 16:08:04 +08:00
xaxtix 4a43f809b3 Update to 8.3.1 2021-12-09 19:28:33 +03:00
luvletter2333 dac6bc4003
Drop CDNDrive support 2021-12-09 13:56:19 +08:00
luvletter2333 159ced5544
bump version to v8.3.0-preview01 2021-12-09 13:00:16 +08:00
luvletter2333 0aa89c87be
update translation 2021-12-09 12:59:33 +08:00
luvletter2333 b8e0cee4cb
Fix openPGP 2021-12-09 12:58:56 +08:00
luvletter2333 e8484a26a1
Fix nofwd 2021-12-09 12:19:47 +08:00
luvletter2333 14d60206f5
Fix direct send 2021-12-09 11:32:00 +08:00
luvletter2333 b16412e075
Fix icon for send without sound 2021-12-09 11:13:59 +08:00
luvletter2333 534d30325e
Temporary fix ban channel in group 2021-12-09 11:13:58 +08:00
luvletter2333 5d800050de
Fix confirm repeat 2021-12-09 11:13:58 +08:00
luvletter2333 246e4823ad
fixes 2021-12-09 11:13:51 +08:00
arm64v8a e7d45120ba Update dev config 2021-12-08 22:18:32 +08:00
NekoInverter e7b9b95371 Fix cannot delete messages that send as channel (official bug) 2021-12-08 22:16:35 +08:00
NekoInverter 9424f76de0 Allow toggle forward restriction for public channels 2021-12-08 21:33:20 +08:00
NekoInverter 4e91347163 Fix some official client bugs 2021-12-08 21:31:21 +08:00
xaxtix dee3203dfa Update to 8.3.0 2021-12-08 20:56:05 +08:00
arm64v8a 3533f7b61e Delete disableProxyWhenVpnEnabled 2021-12-08 20:49:29 +08:00
luvletter2333 d938d7ff9b
Merge official 8.3.0 2021-12-08 20:33:55 +08:00
xaxtix 3477d72367 Update to 8.3.0 2021-12-08 15:02:09 +03:00
xaxtix dcf1c6d4c3 Update to 8.3.0 2021-12-07 16:25:23 +03:00
xaxtix 64c32ace43 Update to 8.3.0 2021-12-07 16:02:02 +03:00
arm64v8a 6d4984b864 Refine public proxy 2021-12-03 21:24:28 +08:00
luvletter2333 2432283df7
update translation 2021-12-02 16:05:56 +08:00
luvletter2333 7e6e1498ec
Remove useless SDK fixes 2021-12-02 16:02:41 +08:00
luvletter2333 5dcfafeca4
Fix reply repeat for sticker 2021-12-02 12:46:43 +08:00
luvletter2333 334294d683
bump version 2021-12-02 12:09:11 +08:00
luvletter2333 1e31c91623
Refine repeat: fix repeat in thread chat,feat: long click: reply repeat, feat: multiple repeat 2021-12-02 12:03:33 +08:00
luvletter2333 2354bc0989
multiple repeat from action bar menu 2021-12-02 11:12:06 +08:00
luvletter2333 0c3a27f20b
better fix 2021-12-02 10:08:18 +08:00
luvletter2333 a6f4408cd7
bump version 2021-12-02 10:01:47 +08:00
luvletter2333 245eca618b
Fix crash 2021-12-02 10:01:19 +08:00
luvletter2333 3522b51b18
bump version to v8.2.7-rc04 2021-12-01 23:34:09 +08:00
luvletter2333 acd5961324
update translation 2021-12-01 21:34:18 +08:00
Алексей Муратов 07ec4d40d5
Fix PGP Signature 2021-12-01 21:13:25 +08:00
arm64v8a 1ec3c9de1a fix 2021-11-28 14:02:57 +08:00
arm64v8a 04e62a0c0a feat: show timestamp in seconds 2021-11-21 23:40:06 +08:00
arm64v8a 762a7b43be Update DnsFactory 2021-11-21 23:40:02 +08:00
arm64v8a 657f4bd66c fix and improve search from user 2021-11-19 12:28:40 +08:00
arm64v8a a5c29444a7 Allow disable instant camera 2021-11-18 23:53:51 +08:00
arm64v8a 1014cfd48d Workaround without storage permissions 2021-11-18 23:52:11 +08:00
arm64v8a f4c5e5d919 Don't show input in pinned messages view
Revert it to offical behaviour.
2021-11-18 20:03:32 +08:00
arm64v8a 34cc2b6e91 Fix translate crash 2021-11-18 19:38:24 +08:00
luvletter2333 3c8ea5d10b
feat: remember all clicked replies 2021-11-18 02:22:54 +08:00
luvletter2333 fb3a2b88fd
bump version 2021-11-18 00:56:49 +08:00
luvletter2333 a8b667084e
Disable NekoXPushService and use tfoss implementation 2021-11-18 00:55:30 +08:00
aarch64 9e2f8c6754
Update proguard-rules.pro 2021-11-17 23:52:45 +08:00
luvletter2333 59a5b6d633
fix play build 2021-11-17 23:33:31 +08:00
luvletter2333 1c60992a42
Add a fallback for empty locale 2021-11-17 23:33:31 +08:00
arm64v8a f13d267d32 bump version 2021-11-17 22:38:27 +08:00
arm64v8a d09b2201e5 Fix PersianCalendar not showing 2021-11-17 22:18:28 +08:00
aarch64 f7c18a91a4
Update README.md 2021-11-17 21:36:59 +08:00
arm64v8a 3ff59f4a7d Update AndroidManifest 2021-11-17 21:23:10 +08:00
arm64v8a 158fc41032 Delete residentNotification
It's useless.
2021-11-17 21:23:10 +08:00
proletarius101 1081f036c3
update translation 2021-11-17 20:40:37 +08:00
proletarius101 302e658b0e
fix backgroundConnection pref read from keepAliveService
`backgroundConnection` pref is read from `keepAliveService` but written to `backgroundConnection` which seems incorrect
2021-11-17 20:30:59 +08:00
luvletter2333 99c7ba1302
Refine Push services (thanks Tfoss Team) 2021-11-17 20:25:33 +08:00
luvletter2333 fb0a01b4d1
upgrade AGP to 7.0.3 and fix builds 2021-11-17 20:18:05 +08:00
luvletter2333 6cb7ce9432
Fix translation 2021-11-16 01:59:09 +08:00
luvletter2333 7ae80a380c
bump version 2021-11-16 00:31:30 +08:00
luvletter2333 a932196248
Fix CDNDrive 2021-11-16 00:31:04 +08:00
luvletter2333 087154ed02
bump version 2021-11-16 00:14:39 +08:00
arm64v8a 87e5cb5211 fix updater 2021-11-16 00:12:50 +08:00
luvletter2333 1a384e7819
Remove empty translations 2021-11-16 00:09:18 +08:00
luvletter2333 d2e681675d
Revert malicious translations 2021-11-16 00:05:35 +08:00
luvletter2333 078720aeeb
Fix myself missing in group participants 2021-11-16 00:00:27 +08:00
arm64v8a edd2b3f224 fix updater 2021-11-15 23:55:12 +08:00
luvletter2333 3af0d658e4
bump version 2021-11-15 22:49:51 +08:00
luvletter2333 6aa2a01b76
Fix official version code 2021-11-15 22:49:11 +08:00
luvletter2333 c245244b29
update translation 2021-11-15 22:30:47 +08:00
luvletter2333 b68bb4932c
Merge official v8.2.7 2021-11-15 22:25:04 +08:00
xaxtix 23b70a3882 Update to 8.2.7 2021-11-15 15:52:32 +03:00
arm64v8a cb22a70c96 confirm before repeat 2021-11-12 23:08:31 +08:00
arm64v8a ca671a7c01 refactor neko stuff in ChatActivity 2021-11-12 23:08:28 +08:00
arm64v8a 4a12bad17c add more restart tips 2021-11-12 22:15:13 +08:00
arm64v8a dd007a01e7 fix 2021-11-12 00:34:30 +08:00
arm64v8a ddf8e8b7ae bump version 2021-11-11 22:50:13 +08:00
arm64v8a 783aa55c5a Fix InternalUpdater 2021-11-11 22:50:13 +08:00
arm64v8a 55edb6bde4 Allow loading custom emoji 2021-11-11 22:13:00 +08:00
arm64v8a ac47e574e0 Fix InternalUpdater & Refactor Cells 2021-11-11 20:36:14 +08:00
arm64v8a 3c8781c7de Update InternalUpdater 2021-11-11 20:36:14 +08:00
arm64v8a 4eacbcf38f Disable AppleEmoji build 2021-11-11 20:36:13 +08:00
luvletter2333 9dee7ae6d2
Merge #670, fix save to gallery 2021-11-11 16:11:40 +08:00
luvletter2333 a097f87677
Refine configs 2021-11-11 14:56:48 +08:00
luvletter2333 c139e60d06
Refactoring Config Cells 2021-11-11 01:24:03 +08:00
arm64v8a 575dc800c2 Unify app dir name 2021-11-10 10:51:24 +08:00
arm64v8a a08f0973a4 Disable NO_SCOPED_STORAGE on Android 11+
This enables system file picker.
Side effects are not tested.
2021-11-09 23:47:37 +08:00
arm64v8a dea17045af bump version 2021-11-09 22:26:45 +08:00
arm64v8a 23e1b288b7 Merge 8.2.3 (2466) 2021-11-09 22:26:23 +08:00
xaxtix 3b971647e2 Update to 8.2.3 2021-11-09 05:38:47 +03:00
xaxtix 44be21dd0f Update to 8.2.3 2021-11-09 04:49:31 +03:00
arm64v8a 28b2b53012 fix OpenCC 2021-11-08 00:18:17 +08:00
arm64v8a f3510d25cd Add confirmation before deleting the account 2021-11-07 23:12:40 +08:00
arm64v8a 0637f83540 Move Android 11+ cache path 2021-11-07 23:12:40 +08:00
arm64v8a 9828ce5de6 UI tweaks 2021-11-06 19:05:46 +08:00
arm64v8a 5696cc55ab fix nullptr in Theme.java 2021-11-06 19:05:46 +08:00
arm64v8a d0bb8abd61 fix disableProxyWhenVpnEnabled 2021-11-06 19:05:46 +08:00
aarch64 2ae3c318ef
Update README.md 2021-11-06 15:39:42 +08:00
arm64v8a 771a87b40e minor fix 2021-11-06 13:23:37 +08:00
arm64v8a 4471aa430a target sdk 30 2021-11-06 13:23:37 +08:00
arm64v8a ecac2aaa96 bump version 2021-11-05 23:25:57 +08:00
arm64v8a 7a9c83d2b8 fix merge 2021-11-05 23:25:57 +08:00
arm64v8a e1098169b6 Merge remote-tracking branch 'tg/master' into dev 2021-11-05 22:11:49 +08:00
arm64v8a 74a2518785 fix 2021-11-05 19:39:34 +08:00
xaxtix 1d379b9a7b Update to 8.2.1 (2462) 2021-11-05 13:06:49 +03:00
luvletter2333 6400ef3a86
Fix test dc login 2021-11-05 01:45:34 +08:00
luvletter2333 6233580e24
Fix test dc handshake 2021-11-05 01:45:34 +08:00
arm64v8a a5ce6c5b91 bump version 2021-11-04 21:44:10 +08:00
arm64v8a 3f4ff5c9ff readd some settings 2021-11-03 15:01:01 +08:00
arm64v8a c972890ddd update translations 2021-11-03 14:28:46 +08:00
Luv Letter ef0f3cd542
Revert ack changes 2021-11-01 21:26:55 +08:00
arm64v8a 099ecfbae1 fix usePersianCalendar 2021-11-01 21:11:36 +08:00
arm64v8a bcfbe63b3d fix migrate 2021-11-01 19:42:33 +08:00
arm64v8a 907f0032ca Refine translate 2021-11-01 19:39:27 +08:00
N de604298a3 Switch order of nameOrder strings
Currently in `Neko Settings > General > Name order`, the `Last First` switch displays names in First name Last name order, and the `First Last` switch displays names in Last name First name order.

Cause:
In https://github.com/NekoX-Dev/NekoX/blob/main/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java#L2644, names are displayed in First name Last name order if `NekomuraConfig.nameOrder.Int() == 1`, else it will be Last name First name order.
The string array has index 0 for `FirstLast` and index 1 for `LastFirst', causing the opposite string to be used.

This fixes it.
2021-11-01 12:48:28 +08:00
luvletter2333 48f9ef024a
update translation 2021-11-01 00:14:26 +08:00
luvletter2333 6aeb6f679e
Fix AudioStream 2021-11-01 00:05:18 +08:00
arm64v8a 837a6543e0 customPublicProxyIP: add description 2021-10-31 23:48:41 +08:00
arm64v8a 11ebf4e639 bump version 2021-10-31 23:07:47 +08:00
arm64v8a 9f288f8999 fix savefile 2021-10-31 23:07:47 +08:00
arm64v8a eaffdc28ad fix 2021-10-31 23:07:47 +08:00
luvletter2333 34c63b4991
Fix ffmpeg build 2021-10-31 23:06:27 +08:00
luvletter2333 ef9a38a104
Fix stream video 2021-10-31 22:09:13 +08:00
luvletter2333 af9c719d69
sync native with tfoss 2021-10-31 21:09:32 +08:00
luvletter2333 054494a475
Merge pull #649 feat: hideGroupSticker, credit @qwq233 2021-10-31 20:00:23 +08:00
arm64v8a dbc331947f bump version 2021-10-31 13:41:39 +08:00
arm64v8a cf5115cafa PhotoViewer: show DC 2021-10-31 12:15:24 +08:00
arm64v8a c2bc81ac5a customPublicProxyIP 2021-10-30 23:06:34 +08:00
arm64v8a de5573c5ad add developer 2021-10-30 23:06:34 +08:00
aarch64 9f686460c2
Update Compilation Guide 2021-10-30 20:48:17 +08:00
arm64v8a 2e9f810e4d Improve DeleteDownloadedFile 2021-10-30 20:14:27 +08:00
arm64v8a 4ff6df9f9a migrate user nekoconfig 2021-10-30 18:22:46 +08:00
arm64v8a c37dfa2831 Refactor NekoConfig & fixies 2021-10-30 18:22:44 +08:00
arm64v8a 44d546c170 Update config keys 2021-10-30 18:22:23 +08:00
arm64v8a f4c919d593 NekoSettings: fix nullptr 2021-10-30 18:22:23 +08:00
arm64v8a da8d2942f0 wip: refactor NekoConfig 2021-10-30 18:22:23 +08:00
arm64v8a 26f3498957 largeAvatarInDrawer & unreadBadgeOnBackButton 2021-10-30 18:22:23 +08:00
arm64v8a d62d686e6c Fix offical nightmode button 2021-10-30 18:22:23 +08:00
arm64v8a a60e43c704 Readd missing button in DrawerProfileCell 2021-10-30 18:22:23 +08:00
arm64v8a b62eed4c9c Try to use DoH to get public proxy 2021-10-30 18:22:20 +08:00
arm64v8a edb9fc5501 New Config 2021-10-28 23:53:34 +08:00
arm64v8a 81c43686ca Improve DisableProxyWhenVpnEnabled 2021-10-28 23:53:34 +08:00
qwq233 9577c1e319
feat: hideGroupSticker
this feature is to hide the group sticker set by the group admins and make the sticker package added by the user appear at the top.
when this feature is enabled, group sticker will be hidden
default setting is false.

Signed-off-by: qwq233 <qwq233@qwq2333.top>
2021-10-23 20:13:46 +08:00
luvletter2333 d8105a7223
bump version 2021-10-20 17:44:34 +08:00
luvletter2333 d491dd5331
update translation 2021-10-20 17:41:30 +08:00
luvletter2333 9c31adf0db
add an option to clean notification channels 2021-10-18 01:30:38 +08:00
luvletter2333 c85b411a3e
Revert "Add groups for notification channel" 2021-10-18 01:14:02 +08:00
luvletter2333 78a8692a67
bump version to v8.1.2 2021-10-08 13:04:35 +08:00
luvletter2333 77e081f98c
update translation 2021-09-30 13:56:48 +08:00
luvletter2333 e4559ae03e
bump version to 8.1.2-preview01 2021-09-30 13:56:18 +08:00
luvletter2333 97d13b90a4
merge official 8.1.2 2021-09-28 01:10:49 +08:00
xaxtix 09f95ec069 Update to 8.1.2 (2432) 2021-09-27 19:38:27 +03:00
luvletter2333 02a95a3ce9
bump version to v8.1.1 2021-09-22 18:14:55 +08:00
luvletter2333 a7c1f6958a
Remove NoQuote Forward and Save Message in schedule mode 2021-09-22 14:02:36 +08:00
luvletter2333 bb58092263
Don't show audioCallIconItem 2021-09-22 00:27:42 +08:00
luvletter2333 36ed1295c9
update translation 2021-09-21 20:29:25 +08:00
luvletter2333 99858655f1
bump version to v8.1.1-preview01 2021-09-21 20:28:43 +08:00
luvletter2333 6ccaf3ff78
fix git 2021-09-21 20:10:44 +08:00
xaxtix 7e91c949bc
merge official update v8.0.1 2021-09-21 20:07:25 +08:00
xaxtix 58aded63e9 Update to 8.1.1 (2431) 2021-09-21 12:51:08 +03:00
世界 a36a9c5980
Remove some features due to official censor 2021-09-21 10:40:05 +08:00
世界 8c0def7556
Update dependencies 2021-09-21 08:21:24 +08:00
luvletter2333 7cc94fa9ea
Fix open from homescreen shortcut 2021-09-21 01:05:53 +08:00
luvletter2333 87a6a5b5e9
Refine codes 2021-09-21 00:36:48 +08:00
luvletter2333 219549f0fd
bump version to v8.0.1-preview03 2021-09-21 00:21:50 +08:00
luvletter2333 5c57c63af0
Refine NekoX Theme 2021-09-21 00:21:01 +08:00
luvletter2333 cead4be034
Refine NekoX Theme 2021-09-20 23:55:08 +08:00
luvletter2333 d5188d73f9
Fix open from homescreen shortcut 2021-09-20 22:56:28 +08:00
luvletter2333 03db127ae1
bump version to v8.0.1-preview02 2021-09-20 21:44:37 +08:00
luvletter2333 972d5b0430
update translation 2021-09-20 21:44:23 +08:00
luvletter2333 b3e16dca87
remember the status of showing accounts 2021-09-20 21:41:25 +08:00
luvletter2333 5ca6f284e4
enable disableSwipeToNext and disableRemoteEmojiInteractions by default 2021-09-20 21:31:40 +08:00
luvletter2333 b6c695022e
Add disable remote emoji interactions and disableChoosingSticker 2021-09-20 21:30:57 +08:00
luvletter2333 1d25b61d10
Refine english name of settings 2021-09-20 21:23:46 +08:00
luvletter2333 c063ee58b5
Fix mystery NPE 2021-09-20 20:53:27 +08:00
luvletter2333 2b1309bb69
Fix change color in pm 2021-09-20 20:51:55 +08:00
luvletter2333 0656bd2ef8
Fix npe when reset to default theme 2021-09-20 19:01:36 +08:00
luvletter2333 bcff1d141e
update translation 2021-09-20 17:20:08 +08:00
luvletter2333 2292c48ea1
change icon back for send without sound 2021-09-20 17:16:27 +08:00
luvletter2333 f6d1807fbe
bump version 2021-09-20 17:14:26 +08:00
luvletter2333 65548549fd
merge official update 8.0.1 2021-09-20 17:14:20 +08:00
xaxtix 42feed0f42 Update to 8.0.1 (2428) 2021-09-20 08:54:41 +03:00
luvletter2333 43001f700a
Fix stringstream in Group VoIP 2021-09-20 13:33:34 +08:00
xaxtix 418f478a62 Update to 8.0.1 (2428) 2021-09-20 01:10:42 +03:00
luvletter2333 af3b235626
Refine UI 2021-09-15 23:24:09 +08:00
luvletter2333 8a080aa535
update translation 2021-09-02 11:21:36 +08:00
luvletter2333 72d85b8a80
update translation 2021-09-02 11:09:56 +08:00
luvletter2333 c62714af5a
use single thread to upload 2021-09-02 10:58:28 +08:00
luvletter2333 be1c61363e
bump version 2021-09-02 10:54:38 +08:00
luvletter2333 007b632c51
add option to disable swipe to next channel 2021-09-02 10:53:37 +08:00
luvletter2333 9125d6e0f3
Fix hideCaption, #619 2021-09-01 17:29:30 +08:00
luvletter2333 54295812ec
update translation 2021-09-01 15:32:27 +08:00
luvletter2333 bb2dff85cd
bump version 2021-09-01 15:31:05 +08:00
luvletter2333 2e99b4e41d
fix no quote forward 2021-09-01 15:22:00 +08:00
luvletter2333 986762f504
Merge official update v8.0.0 2021-09-01 15:10:12 +08:00
DrKLO 368822d20f Update to 8.0.0 (2406) 2021-08-31 22:06:39 +03:00
luvletter2333 087c7b850e
update translation 2021-08-29 00:01:16 +08:00
luvletter2333 181fba47ae
bump version 2021-08-28 23:55:23 +08:00
luvletter2333 3cb50b03be
fix translator when using internal proxy 2021-08-28 23:54:09 +08:00
luvletter2333 87eab8f0df
allow setting max recent sticker count, add padding for removing favorite stickers 2021-08-28 22:05:34 +08:00
luvletter2333 891a10e69c
Custom group voip audio bitrate 2021-08-21 04:23:27 +08:00
luvletter2333 94a130346c
save to gallery in sticker preview 2021-08-20 16:13:57 +08:00
luvletter2333 50ee1ecd98
save transparent PNG for stickers 2021-08-20 04:51:51 +08:00
luvletter2333 31a35ce721
Disable switchToPip by default 2021-08-20 04:04:01 +08:00
luvletter2333 f0761cb93c
fix DNS cache 2021-08-20 03:17:32 +08:00
luvletter2333 df23ecad49
add useSystemDNS & customDoH 2021-08-20 03:10:27 +08:00
luvletter2333 ad62860829
fix Details 2021-08-20 03:09:22 +08:00
luvletter2333 5b8479f679
bump version 2021-08-09 19:30:19 +08:00
luvletter2333 cd6cbd8afe
Try to get debug build 2021-08-09 18:03:56 +08:00
luvletter2333 f527e84247
Fix twitter emoji 2021-08-09 18:02:38 +08:00
luvletter2333 e8179cd090
bump version 2021-08-08 17:30:54 +08:00
luvletter2333 3822b50f4b
Fix database version 2021-08-08 17:19:59 +08:00
luvletter2333 b30dcf0479
Fix uncommitted emoji file 2021-08-08 16:09:36 +08:00
luvletter2333 332bd58746
Fix forwarded message in linked group 2021-08-08 02:31:23 +08:00
luvletter2333 f2780a2acc
Fix talkback for search button, #582 2021-08-08 00:34:34 +08:00
luvletter2333 f4453c1377
Try to fix bad proxy db, #591 2021-08-08 00:08:14 +08:00
luvletter2333 d87666c9c1
merge official v7.9.2 & v7.9.3 2021-08-07 17:28:34 +08:00
DrKLO ab221dafad Update to 7.9.3 (2390) 2021-08-07 02:55:04 +03:00
DrKLO fc12550efb Update to 7.9.2 (2389) 2021-08-05 23:35:18 +03:00
Riko Sakurauchi 255bb50555
fix wrong tab dialogs after forward (credit. satouriko) 2021-08-04 15:44:30 +08:00
luvletter2333 c5989d43ca
Fix QRCode login and test dc 2021-08-04 15:15:40 +08:00
luvletter2333 feac2daf64
bump version 2021-08-04 13:17:43 +08:00
luvletter2333 f64b8055e5
Fix send comment after forward 2021-08-04 01:05:55 +08:00
luvletter2333 256445c91f
bump version 2021-08-03 18:09:01 +08:00
luvletter2333 32097cc6e0
Fix wrong tabs after forward 2021-08-03 18:08:27 +08:00
luvletter2333 0fd1f7ba12 Fix audio encoding 2021-08-01 14:10:40 -04:00
luvletter2333 160d644695 Fix play in internal updater 2021-08-01 10:07:23 -04:00
luvletter2333 1cc03ad060 merge upstream v7.9.0 & v7.9.1 2021-08-01 09:31:28 -04:00
DrKLO 24c6968b8a Update to 7.9.1 (2387) 2021-07-31 06:39:13 +07:00
DrKLO 3e5d2ba92b Update to 7.9.0 (2384) 2021-07-30 21:49:55 +07:00
luvletter2333 5d1583e35b
Fix compile sdk in play publish 2021-07-27 12:26:10 +08:00
luvletter2333 e6619a7a0e
downgrade robolectric 2021-07-27 03:40:08 +08:00
luvletter2333 0f6b02ac5f
fix ci 2021-07-27 03:09:14 +08:00
luvletter2333 13e630d832
try to fix build 2021-07-27 03:01:42 +08:00
luvletter2333 432ecbe331
try to fix build 2021-07-27 02:36:36 +08:00
luvletter2333 717581e722
upgrade dependencies 2021-07-27 02:27:01 +08:00
luvletter2333 01aedba9a8
merge upstream v7.8.2 2021-07-26 21:41:28 +08:00
DrKLO 7a60f948ae Update to 7.8.2 (2376)
Thanks to @alabiaga and @marcelpinto for help with improvements for Android 12.
https://github.com/DrKLO/Telegram/pull/1633
https://github.com/DrKLO/Telegram/pull/1636
https://github.com/DrKLO/Telegram/pull/1630
2021-07-19 18:56:43 +03:00
luvletter2333 b0f64e4eb1
feat: Hide time for stickers 2021-07-16 14:10:34 +08:00
luvletter2333 17dd08d9ba
merge official v7.8.1 2021-07-16 14:10:08 +08:00
DrKLO 3ac3c37dd2 Update to 7.8.1 2021-07-15 17:24:57 +03:00
luvletter2333 90e8b77849
bump version 2021-07-07 01:03:57 +08:00
luvletter2333 bc6525c56e
Merge AndroidManifest again 2021-07-07 01:02:42 +08:00
luvletter2333 e97bc91323
bump version 2021-07-06 21:23:40 +08:00
luvletter2333 ec29db5acf
try fix updater 2021-07-06 21:23:27 +08:00
luvletter2333 ee12252e5b
fix option in action.VIEW 2021-06-30 19:33:07 +08:00
15454 changed files with 1096835 additions and 256681 deletions

164
.github/scripts/upload.py vendored Normal file
View File

@ -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")

View File

@ -11,6 +11,11 @@ on:
pull_request:
branches:
- dev
workflow_dispatch:
inputs:
artifacts:
description: 'y, if need artifacts'
required: false
jobs:
ffmpeg:
@ -20,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'
@ -40,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)
@ -89,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
@ -107,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'
@ -236,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
@ -300,7 +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
# echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" >> local.properties
- name: Gradle cache
uses: actions/cache@v2
with:
@ -312,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

View File

@ -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,7 +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
# echo "ndk.dir=${ANDROID_HOME}/ndk-bundle" > local.properties
- name: Gradle cache
uses: actions/cache@v2
with:
@ -358,110 +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: 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

4
.gitmodules vendored
View File

@ -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

View File

@ -1,27 +0,0 @@
FROM gradle:6.7.1-jdk8
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
ENV ANDROID_API_LEVEL android-30
ENV ANDROID_BUILD_TOOLS_VERSION 30.0.3
ENV ANDROID_HOME /usr/local/android-sdk-linux
ENV ANDROID_NDK_VERSION 21.4.7075529
ENV ANDROID_VERSION 30
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}/tools/bin/sdkmanager --licenses
RUN $ANDROID_HOME/tools/bin/sdkmanager --update
RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \
"platforms;android-${ANDROID_VERSION}" \
"platform-tools" \
"ndk;$ANDROID_NDK_VERSION"
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

138
README.md
View File

@ -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
- 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
- Add stickers without sticker pack
- Allow disabling vibration
- Allow clicking on links in self profile
- Sticker set list backup / restore / share
- Full InstantView translation support
- Translation support for selected text on input and in messages
- Delete all messages in group
- 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 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
- 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
- **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
- 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
- Sticker set list backup / restore / share
- **Internationalization**
- OpenCC Chinese Convert
- Full InstantView translation support
- Translation support for selected text on input and in messages
- 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
- 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).
- Custom cache directory (supports external storage)
- Custom server (official, test DC)
- Option to block others from starting a secret chat with you
- Option to disable trending
- **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
- 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.
@ -182,4 +194,4 @@ You can [open an issue to](https://github.com/NekoX-Dev/NekoX/issues/new?&templa
<li>JetBrains: for allocating free open-source licences for IDEs</li>
</ul>
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://jb.gg/OpenSource)
[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://jb.gg/OpenSource)

View File

@ -3,15 +3,15 @@ import cn.hutool.core.util.RuntimeUtil
apply plugin: "com.android.application"
apply plugin: "kotlin-android"
def verName = "7.8.0-preview02"
def verCode = 330
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.8.0"
def officialCode = 2360
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 30
buildToolsVersion "30.0.3"
compileSdkVersion 33
buildToolsVersion "33.0.0"
ndkVersion rootProject.ext.ndkVersion
defaultConfig.applicationId = "nekox.messenger"
@ -87,29 +81,31 @@ android {
splits {
abi {
enable true
universalApk false
if (!targetAbi.isBlank()) {
reset()
if (targetAbi == "arm64") {
include "arm64-v8a"
} else if (targetAbi == "arm") {
include "armeabi-v7a"
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
reset()
include nativeTarget
} else {
enable true
universalApk false
}
} else if (!nativeTarget.isBlank()) {
reset()
include nativeTarget
}
}
}
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,66 +312,61 @@ 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.0.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-beta01"
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.2"
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.13.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.10"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
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.3.1"
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.6.5"
implementation "cn.hutool:hutool-crypto:5.6.5"
implementation "cn.hutool:hutool-http:5.6.5"
implementation "com.jakewharton:process-phoenix:2.0.0"
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.1.2"
implementation 'com.google.guava:guava:31.1-android'
compileOnly "org.yaml:snakeyaml:1.28"
fullImplementation "org.yaml:snakeyaml:1.28"
compileOnly 'org.yaml:snakeyaml:1.29'
fullImplementation 'org.yaml:snakeyaml:1.29'
implementation project(":openpgp-api")
@ -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.3.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
@ -461,4 +425,4 @@ android {
}
}
}
}

View File

@ -5,21 +5,31 @@ 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 ()
else()
enable_language(ASM_NASM)
endif ()
endif()
add_library(avutil STATIC IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${CMAKE_HOME_DIRECTORY}/ffmpeg/build/${ANDROID_ABI}/lib/libavutil.a)
@ -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.39")
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
@ -718,7 +769,7 @@ target_include_directories(${NATIVE_LIB} PUBLIC
lz4)
target_link_libraries(${NATIVE_LIB}
-Wl,--whole-archive rnnoise voipandroid -Wl,--no-whole-archive
-Wl,--whole-archive rnnoise openh264 voipandroid -Wl,--no-whole-archive
tgvoip
tgcalls
tgcalls_tp
@ -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()

View File

@ -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;
@ -65,10 +66,10 @@ jobject getJavaByteBuffer(JNIEnv *env, jclass c, jlong address) {
static const char *NativeByteBufferClassPathName = "org/telegram/tgnet/NativeByteBuffer";
static JNINativeMethod NativeByteBufferMethods[] = {
{"native_getFreeBuffer", "(I)J", (void *) getFreeBuffer},
{"native_limit", "(J)I", (void *) limit},
{"native_position", "(J)I", (void *) position},
{"native_reuse", "(J)V", (void *) reuse},
{"native_getFreeBuffer", "(I)J", (void *) getFreeBuffer},
{"native_limit", "(J)I", (void *) limit},
{"native_position", "(J)I", (void *) position},
{"native_reuse", "(J)V", (void *) reuse},
{"native_getJavaByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void *) getJavaByteBuffer}
};
@ -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,12 +213,12 @@ 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);
}
void switchBackend(JNIEnv *env, jclass c, jint instanceNum) {
ConnectionsManager::getInstance(instanceNum).switchBackend();
void switchBackend(JNIEnv *env, jclass c, jint instanceNum, jboolean restart) {
ConnectionsManager::getInstance(instanceNum).switchBackend(restart);
}
void pauseNetwork(JNIEnv *env, jclass c, jint instanceNum) {
@ -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,12 +528,12 @@ 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},
{"native_switchBackend", "(I)V", (void *) switchBackend},
{"native_switchBackend", "(IZ)V", (void *) switchBackend},
{"native_pauseNetwork", "(I)V", (void *) pauseNetwork},
{"native_resumeNetwork", "(IZ)V", (void *) resumeNetwork},
{"native_updateDcSettings", "(I)V", (void *) updateDcSettings},
@ -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;
}

View File

@ -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

View File

@ -7,31 +7,38 @@ 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 .
cd ..
}
function checkPreRequisites {
if ! [ -d "boringssl" ] || ! [ "$(ls -A boringssl)" ]; then
echo -e "\033[31mFailed! Submodule 'boringssl' not found!\033[0m"
echo -e "\033[31mTry to run: 'git submodule init && git submodule update'\033[0m"
exit
fi
if ! [ -d "boringssl" ] || ! [ "$(ls -A boringssl)" ]; then
echo -e "\033[31mFailed! Submodule 'boringssl' 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
if [ -z "$NINJA_PATH" -a "$NINJA_PATH" == "" ]; then
echo -e "\033[31mFailed! NINJA_PATH is empty. Run 'export NINJA_PATH=[PATH_TO_NINJA]'\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
if [ -z "$NINJA_PATH" -a "$NINJA_PATH" == "" ]; then
echo -e "\033[31mFailed! NINJA_PATH is empty. Run 'export NINJA_PATH=[PATH_TO_NINJA]'\033[0m"
exit
fi
}
ANDROID_NDK=$NDK
@ -39,20 +46,41 @@ checkPreRequisites
cd boringssl
rm -rf build
mkdir build
cd build
API=16
function build {
for arg in "$@"; do
case "${arg}" in
x86_64)
API=21
CPU=x86_64
build_one
;;
arm64)
API=21
CPU=arm64-v8a
build_one
;;
arm)
API=16
CPU=armeabi-v7a
build_one
;;
x86)
API=16
CPU=x86
build_one
;;
*)
;;
esac
done
}
CPU=armeabi-v7a
build_one
CPU=x86
build_one
API=21
CPU=arm64-v8a
build_one
CPU=x86_64
build_one
if (( $# == 0 )); then
build x86_64 arm64 arm x86
else
build $@
fi

View File

@ -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

View File

@ -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,18 +38,19 @@ 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 \
--enable-static \
--enable-asm \
--enable-inline-asm \
--enable-x86asm \
--enable-asm \
--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,50 +97,44 @@ 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
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
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}"
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 ! [ -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
if [ -z "$NDK" -a "$NDK" == "" ]; then
echo -e "\033[31mFailed! NDK is empty. Run 'export NDK=[PATH_TO_NDK]'\033[0m"
exit
fi
}
setCurrentPlatform
@ -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
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
CPU=x86_64
PREFIX=./build/$CPU
ADDITIONAL_CONFIGURE_FLAG="--disable-asm"
build_one
function build {
for arg in "$@"; do
case "${arg}" in
x86_64)
ANDROID_API=21
#arm64-v8a
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
CPU=arm64-v8a
OPTIMIZE_CFLAGS=
PREFIX=./build/$CPU
ADDITIONAL_CONFIGURE_FLAG="--enable-neon --enable-optimizations"
build_one
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)
ANDROID_API=21
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)
ANDROID_API=16
#arm v7n
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
CPU=armv7-a
OPTIMIZE_CFLAGS="-marm -march=$CPU"
PREFIX=./build/armeabi-v7a
ADDITIONAL_CONFIGURE_FLAG="--enable-neon"
build_one
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)
ANDROID_API=16
#x86 platform
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
CPU=i686
OPTIMIZE_CFLAGS="-march=$CPU"
PREFIX=./build/x86
ADDITIONAL_CONFIGURE_FLAG="--disable-x86asm --disable-inline-asm --disable-asm"
build_one
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

View File

@ -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

View File

@ -19,18 +19,20 @@ extern "C" {
#include <libavformat/isom.h>
#include <libavcodec/bytestream.h>
#include <libavcodec/get_bits.h>
#include <libavcodec/golomb.h>
#include <libavcodec/golomb.h>
#include <libavutil/eval.h>
#include <libavutil/intmath.h>
#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);
return (std::string) errbuf;
}
#undef av_err2str
#define av_err2str(errnum) av_make_error_str(errnum).c_str()
#define FFMPEG_AVSEEK_SIZE 0x10000
@ -89,7 +91,7 @@ void ff_h2645_packet_uninit(H2645Packet *pkt) {
}
typedef struct VideoInfo {
~VideoInfo() {
if (video_dec_ctx) {
avcodec_close(video_dec_ctx);
@ -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;
@ -683,7 +698,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, int &width, int &height)
}
return mb_width != width || mb_height != height;
}
int decode_packet(VideoInfo *info, int *got_frame) {
int ret = 0;
int decoded = info->pkt.size;
@ -709,7 +724,7 @@ int decode_packet(VideoInfo *info, int *got_frame) {
}
}
}
return decoded;
}
@ -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;
}
}
}
@ -922,7 +943,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDr
extern "C" JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv *env, jclass clazz, jstring src, jintArray data, jint account, jlong streamFileSize, jobject stream, jboolean preview) {
VideoInfo *info = new VideoInfo();
char const *srcString = env->GetStringUTFChars(src, 0);
size_t len = strlen(srcString);
info->src = new char[len + 1];
@ -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);
@ -969,13 +991,13 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_AnimatedFileD
return 0;
}
}
if ((ret = avformat_find_stream_info(info->fmt_ctx, NULL)) < 0) {
LOGE("can't find stream information %s, %s", info->src, av_err2str(ret));
delete info;
return 0;
}
if (open_codec_context(&info->video_stream_idx, &info->video_dec_ctx, info->fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
info->video_stream = info->fmt_ctx->streams[info->video_stream_idx];
}
@ -985,14 +1007,14 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_AnimatedFileD
delete info;
return 0;
}
info->frame = av_frame_alloc();
if (info->frame == nullptr) {
LOGE("can't allocate frame %s", info->src);
delete info;
return 0;
}
av_init_packet(&info->pkt);
info->pkt.data = NULL;
info->pkt.size = 0;
@ -1016,9 +1038,9 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_AnimatedFileD
//(int32_t) (1000 * info->video_stream->duration * av_q2d(info->video_stream->time_base));
env->ReleaseIntArrayElements(data, dataArr, 0);
}
//LOGD("successfully opened file %s", info->src);
return (jlong) (intptr_t) info;
}
@ -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;

View File

@ -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);
@ -1182,7 +1188,7 @@ std::vector<std::pair<float, float>> gatherPositions(std::vector<std::pair<float
return result;
}
static float *pixelCache = nullptr;
thread_local static float *pixelCache = nullptr;
JNIEXPORT void Java_org_telegram_messenger_Utilities_generateGradient(JNIEnv *env, jclass clazz, jobject bitmap, jboolean unpin, jint phase, jfloat progress, jint width, jint height, jint stride, jintArray colors) {
if (!bitmap) {
@ -1217,29 +1223,29 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_generateGradient(JNIEnv *en
std::vector<std::pair<float, float>> current = gatherPositions(positions, phase);
auto colorsArray = (uint8_t *) env->GetIntArrayElements(colors, nullptr);
/*float *newPixelCache = nullptr;
float *newPixelCache = nullptr;
if (pixelCache == nullptr) {
newPixelCache = new float[width * height * 2];
}*/
}
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) {
if (pixelCache == nullptr) {
directPixelY = (float) y / (float) height;
centerDistanceY = directPixelY - 0.5f;
centerDistanceY2 = centerDistanceY * centerDistanceY;
//}
}
uint32_t offset = y * stride;
for (int x = 0; x < width; x++) {
float pixelX;
float pixelY;
/*if (pixelCache != nullptr) {
if (pixelCache != nullptr) {
pixelX = pixelCache[(y * width + x) * 2];
pixelX = pixelCache[(y * width + x) * 2 + 1];
} else {*/
pixelY = pixelCache[(y * width + x) * 2 + 1];
} else {
float directPixelX = (float) x / (float) width;
float centerDistanceX = directPixelX - 0.5f;
@ -1250,9 +1256,9 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_generateGradient(JNIEnv *en
float sinTheta = sinf(theta);
float cosTheta = cosf(theta);
pixelX = /*newPixelCache[(y * width + x) * 2] =*/ std::max(0.0f, std::min(1.0f, 0.5f + centerDistanceX * cosTheta - centerDistanceY * sinTheta));
pixelY = /*newPixelCache[(y * width + x) * 2 + 1] =*/ std::max(0.0f, std::min(1.0f, 0.5f + centerDistanceX * sinTheta + centerDistanceY * cosTheta));
//}
pixelX = newPixelCache[(y * width + x) * 2] = std::max(0.0f, std::min(1.0f, 0.5f + centerDistanceX * cosTheta - centerDistanceY * sinTheta));
pixelY = newPixelCache[(y * width + x) * 2 + 1] = std::max(0.0f, std::min(1.0f, 0.5f + centerDistanceX * sinTheta + centerDistanceY * cosTheta));
}
float distanceSum = 0.0f;
@ -1282,10 +1288,10 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_generateGradient(JNIEnv *en
pixels[offset + x * 4 + 3] = 0xff;
}
}
/*if (newPixelCache != nullptr) {
if (newPixelCache != nullptr) {
delete [] pixelCache;
pixelCache = newPixelCache;
}*/
}
env->ReleaseIntArrayElements(colors, (jint *) colorsArray, JNI_ABORT);

View File

@ -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;
stars[i].position.z += 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) {

View File

@ -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

View File

@ -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;
}
}
Surface surface((uint32_t *) pixels, (size_t) w, (size_t) h, (size_t) stride);
info->animation->renderSync((size_t) frame, surface, clear, &result);
AndroidBitmap_unlockPixels(env, bitmap);
}
if (!result) {
return -5;
}
return frame;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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
+

View File

@ -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

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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()) {

View File

@ -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

View File

@ -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>
** &nbsp; unsigned int demonstration_autovac_pages_callback(
** &nbsp; void *pClientData,
** &nbsp; const char *zSchema,
** &nbsp; unsigned int nDbPage,
** &nbsp; unsigned int nFreePage,
** &nbsp; unsigned int nBytePerPage
** &nbsp; ){
** &nbsp; return nFreePage;
** &nbsp; }
** </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,24 +7164,56 @@ 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
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_LIKE 65
#define SQLITE_INDEX_CONSTRAINT_GLOB 66
#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
#define SQLITE_INDEX_CONSTRAINT_NE 68
#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
#define SQLITE_INDEX_CONSTRAINT_IS 72
#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_LIKE 65
#define SQLITE_INDEX_CONSTRAINT_GLOB 66
#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
#define SQLITE_INDEX_CONSTRAINT_NE 68
#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
#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
/*
** CAPI3REF: Register A Virtual Table Implementation
@ -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>
** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal);
** &nbsp; rc==SQLITE_OK && pVal
** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal)
** &nbsp; ){
** &nbsp; // do something with pVal
** &nbsp; }
** &nbsp; if( rc!=SQLITE_OK ){
** &nbsp; // an error has occurred
** &nbsp; }
** </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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -502,6 +502,15 @@ void ConnectionSocket::openConnectionInternal(bool ipv6) {
if (setsockopt(socketFd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int))) {
if (LOGS_ENABLED) DEBUG_E("connection(%p) set TCP_NODELAY failed", this);
}
#ifdef DEBUG_VERSION
int size = 4 * 1024 * 1024;
if (setsockopt(socketFd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(int))) {
if (LOGS_ENABLED) DEBUG_E("connection(%p) set SO_SNDBUF failed", this);
}
if (setsockopt(socketFd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(int))) {
if (LOGS_ENABLED) DEBUG_E("connection(%p) set SO_RCVBUF failed", this);
}
#endif
if (fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
if (LOGS_ENABLED) DEBUG_E("connection(%p) set O_NONBLOCK failed", this);

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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 switchBackend();
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

View File

@ -32,27 +32,27 @@ thread_local static SHA256_CTX sha256Ctx;
Datacenter::Datacenter(int32_t instance, uint32_t id) {
instanceNum = instance;
datacenterId = id;
for (uint32_t a = 0; a < UPLOAD_CONNECTIONS_COUNT; a++) {
uploadConnection[a] = nullptr;
for (auto & a : uploadConnection) {
a = nullptr;
}
for (uint32_t a = 0; a < DOWNLOAD_CONNECTIONS_COUNT; a++) {
downloadConnection[a] = nullptr;
for (auto & a : downloadConnection) {
a = nullptr;
}
for (uint32_t a = 0; a < PROXY_CONNECTIONS_COUNT; a++) {
proxyConnection[a] = nullptr;
for (auto & a : proxyConnection) {
a = nullptr;
}
}
Datacenter::Datacenter(int32_t instance, NativeByteBuffer *data) {
instanceNum = instance;
for (uint32_t a = 0; a < UPLOAD_CONNECTIONS_COUNT; a++) {
uploadConnection[a] = nullptr;
for (auto & a : uploadConnection) {
a = nullptr;
}
for (uint32_t a = 0; a < DOWNLOAD_CONNECTIONS_COUNT; a++) {
downloadConnection[a] = nullptr;
for (auto & a : downloadConnection) {
a = nullptr;
}
for (uint32_t a = 0; a < PROXY_CONNECTIONS_COUNT; a++) {
proxyConnection[a] = nullptr;
for (auto & a : proxyConnection) {
a = nullptr;
}
uint32_t currentVersion = data->readUint32(nullptr);
if (currentVersion >= 2 && currentVersion <= configVersion) {
@ -146,7 +146,7 @@ Datacenter::Datacenter(int32_t instance, NativeByteBuffer *data) {
authorized = data->readInt32(nullptr) != 0;
len = data->readUint32(nullptr);
for (uint32_t a = 0; a < len; a++) {
TL_future_salt *salt = new TL_future_salt();
auto salt = new TL_future_salt();
salt->valid_since = data->readInt32(nullptr);
salt->valid_until = data->readInt32(nullptr);
salt->salt = data->readInt64(nullptr);
@ -155,7 +155,7 @@ Datacenter::Datacenter(int32_t instance, NativeByteBuffer *data) {
if (currentVersion >= 13) {
len = data->readUint32(nullptr);
for (uint32_t a = 0; a < len; a++) {
TL_future_salt *salt = new TL_future_salt();
auto salt = new TL_future_salt();
salt->valid_since = data->readInt32(nullptr);
salt->valid_until = data->readInt32(nullptr);
salt->salt = data->readInt64(nullptr);
@ -223,9 +223,9 @@ TcpAddress *Datacenter::getCurrentAddress(uint32_t flags) {
return nullptr;
}
if ((flags & TcpAddressFlagStatic) != 0) {
for (std::vector<TcpAddress>::iterator iter = addresses->begin(); iter != addresses->end(); iter++) {
if ((iter->flags & TcpAddressFlagStatic) != 0) {
return &(*iter);
for (auto & addresse : *addresses) {
if ((addresse.flags & TcpAddressFlagStatic) != 0) {
return &addresse;
}
}
}
@ -288,8 +288,8 @@ int32_t Datacenter::getCurrentPort(uint32_t flags) {
if ((flags & TcpAddressFlagStatic) != 0) {
uint32_t num = 0;
for (std::vector<TcpAddress>::iterator iter = addresses->begin(); iter != addresses->end(); iter++) {
if ((iter->flags & TcpAddressFlagStatic) != 0) {
for (auto & addresse : *addresses) {
if ((addresse.flags & TcpAddressFlagStatic) != 0) {
currentAddressNum = num;
break;
}
@ -362,8 +362,8 @@ void Datacenter::addAddressAndPort(std::string address, uint32_t port, uint32_t
addresses = &addressesIpv4;
}
}
for (std::vector<TcpAddress>::iterator iter = addresses->begin(); iter != addresses->end(); iter++) {
if (iter->address == address && iter->port == port) {
for (auto & addresse : *addresses) {
if (addresse.address == address && addresse.port == port) {
return;
}
}
@ -414,6 +414,7 @@ void Datacenter::nextAddressOrPort(uint32_t flags) {
if (currentAddressNum + 1 < addresses->size()) {
currentAddressNum++;
} else {
repeatCheckingAddresses = true;
currentAddressNum = 0;
}
currentPortNum = 0;
@ -749,14 +750,14 @@ void Datacenter::suspendConnections(bool suspendPush) {
if (tempConnection != nullptr) {
tempConnection->suspendConnection();
}
for (uint32_t a = 0; a < UPLOAD_CONNECTIONS_COUNT; a++) {
if (uploadConnection[a] != nullptr) {
uploadConnection[a]->suspendConnection();
for (auto & a : uploadConnection) {
if (a != nullptr) {
a->suspendConnection();
}
}
for (uint32_t a = 0; a < DOWNLOAD_CONNECTIONS_COUNT; a++) {
if (downloadConnection[a] != nullptr) {
downloadConnection[a]->suspendConnection();
for (auto & a : downloadConnection) {
if (a != nullptr) {
a->suspendConnection();
}
}
}
@ -771,19 +772,19 @@ void Datacenter::getSessions(std::vector<int64_t> &sessions) {
if (tempConnection != nullptr) {
sessions.push_back(tempConnection->getSessionId());
}
for (uint32_t a = 0; a < UPLOAD_CONNECTIONS_COUNT; a++) {
if (uploadConnection[a] != nullptr) {
sessions.push_back(uploadConnection[a]->getSessionId());
for (auto & a : uploadConnection) {
if (a != nullptr) {
sessions.push_back(a->getSessionId());
}
}
for (uint32_t a = 0; a < DOWNLOAD_CONNECTIONS_COUNT; a++) {
if (downloadConnection[a] != nullptr) {
sessions.push_back(downloadConnection[a]->getSessionId());
for (auto & a : downloadConnection) {
if (a != nullptr) {
sessions.push_back(a->getSessionId());
}
}
for (uint32_t a = 0; a < PROXY_CONNECTIONS_COUNT; a++) {
if (proxyConnection[a] != nullptr) {
sessions.push_back(proxyConnection[a]->getSessionId());
for (auto & a : proxyConnection) {
if (a != nullptr) {
sessions.push_back(a->getSessionId());
}
}
}
@ -796,21 +797,21 @@ void Datacenter::recreateSessions(HandshakeType type) {
if (tempConnection != nullptr) {
tempConnection->recreateSession();
}
for (uint32_t a = 0; a < UPLOAD_CONNECTIONS_COUNT; a++) {
if (uploadConnection[a] != nullptr) {
uploadConnection[a]->recreateSession();
for (auto & a : uploadConnection) {
if (a != nullptr) {
a->recreateSession();
}
}
for (uint32_t a = 0; a < PROXY_CONNECTIONS_COUNT; a++) {
if (proxyConnection[a] != nullptr) {
proxyConnection[a]->recreateSession();
for (auto & a : proxyConnection) {
if (a != nullptr) {
a->recreateSession();
}
}
}
if (type == HandshakeTypeAll || type == HandshakeTypeMediaTemp || type == HandshakeTypePerm) {
for (uint32_t a = 0; a < DOWNLOAD_CONNECTIONS_COUNT; a++) {
if (downloadConnection[a] != nullptr) {
downloadConnection[a]->recreateSession();
for (auto & a : downloadConnection) {
if (a != nullptr) {
a->recreateSession();
}
}
if (genericMediaConnection != nullptr) {
@ -883,8 +884,8 @@ bool Datacenter::isHandshaking(bool media) {
if (media && (isCdnDatacenter || !PFS_ENABLED)) {
media = false;
}
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
if (handshake->getType() == HandshakeTypePerm || (media && handshake->getType() == HandshakeTypeMediaTemp) || (!media && handshake->getType() != HandshakeTypeMediaTemp)) {
return true;
}
@ -896,8 +897,8 @@ bool Datacenter::isHandshaking(HandshakeType type) {
if (handshakes.empty()) {
return false;
}
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
if (handshake->getType() == type) {
return true;
}
@ -907,28 +908,28 @@ bool Datacenter::isHandshaking(HandshakeType type) {
void Datacenter::beginHandshake(HandshakeType handshakeType, bool reconnect) {
if (handshakeType == HandshakeTypeCurrent) {
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
handshake->beginHandshake(reconnect);
}
} else {
if (authKeyPerm == nullptr) {
if (!isHandshaking(HandshakeTypePerm)) {
Handshake *handshake = new Handshake(this, HandshakeTypePerm, this);
auto handshake = new Handshake(this, HandshakeTypePerm, this);
handshakes.push_back(std::unique_ptr<Handshake>(handshake));
handshake->beginHandshake(reconnect);
}
} else if (PFS_ENABLED) {
if (handshakeType == HandshakeTypeAll || handshakeType == HandshakeTypeTemp) {
if (!isHandshaking(HandshakeTypeTemp)) {
Handshake *handshake = new Handshake(this, HandshakeTypeTemp, this);
auto handshake = new Handshake(this, HandshakeTypeTemp, this);
handshakes.push_back(std::unique_ptr<Handshake>(handshake));
handshake->beginHandshake(reconnect);
}
}
if ((handshakeType == HandshakeTypeAll || handshakeType == HandshakeTypeMediaTemp) && hasMediaAddress()) {
if (!isHandshaking(HandshakeTypeMediaTemp)) {
Handshake *handshake = new Handshake(this, HandshakeTypeMediaTemp, this);
auto handshake = new Handshake(this, HandshakeTypeMediaTemp, this);
handshakes.push_back(std::unique_ptr<Handshake>(handshake));
handshake->beginHandshake(reconnect);
}
@ -942,9 +943,9 @@ void Datacenter::onHandshakeConnectionClosed(Connection *connection) {
return;
}
bool media = connection->getConnectionType() == ConnectionTypeGenericMedia;
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
if (media && handshake->getType() == HandshakeTypeMediaTemp || !media && handshake->getType() != HandshakeTypeMediaTemp) {
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
if ((media && handshake->getType() == HandshakeTypeMediaTemp) || (!media && handshake->getType() != HandshakeTypeMediaTemp)) {
handshake->onHandshakeConnectionClosed();
}
}
@ -955,9 +956,9 @@ void Datacenter::onHandshakeConnectionConnected(Connection *connection) {
return;
}
bool media = connection->getConnectionType() == ConnectionTypeGenericMedia;
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
if (media && handshake->getType() == HandshakeTypeMediaTemp || !media && handshake->getType() != HandshakeTypeMediaTemp) {
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
if ((media && handshake->getType() == HandshakeTypeMediaTemp) || (!media && handshake->getType() != HandshakeTypeMediaTemp)) {
handshake->onHandshakeConnectionConnected();
}
}
@ -986,9 +987,9 @@ void Datacenter::processHandshakeResponse(bool media, TLObject *message, int64_t
if (handshakes.empty()) {
return;
}
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
if (media && handshake->getType() == HandshakeTypeMediaTemp || !media && handshake->getType() != HandshakeTypeMediaTemp) {
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
if ((media && handshake->getType() == HandshakeTypeMediaTemp) || (!media && handshake->getType() != HandshakeTypeMediaTemp)) {
handshake->processHandshakeResponse(message, messageId);
}
}
@ -998,9 +999,9 @@ TLObject *Datacenter::getCurrentHandshakeRequest(bool media) {
if (handshakes.empty()) {
return nullptr;
}
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
if (media && handshake->getType() == HandshakeTypeMediaTemp || !media && handshake->getType() != HandshakeTypeMediaTemp) {
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
if ((media && handshake->getType() == HandshakeTypeMediaTemp) || (!media && handshake->getType() != HandshakeTypeMediaTemp)) {
return handshake->getCurrentHandshakeRequest();
}
}
@ -1069,9 +1070,9 @@ ByteArray *Datacenter::getAuthKey(ConnectionType connectionType, bool perm, int6
bool media = Connection::isMediaConnectionType(connectionType) && hasMediaAddress();
ByteArray *authKeyPending = nullptr;
int64_t authKeyPendingId = 0;
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
Handshake *handshake = iter->get();
if (media && handshake->getType() == HandshakeTypeMediaTemp || !media && handshake->getType() == HandshakeTypeTemp) {
for (auto & iter : handshakes) {
Handshake *handshake = iter.get();
if ((media && handshake->getType() == HandshakeTypeMediaTemp) || (!media && handshake->getType() == HandshakeTypeTemp)) {
authKeyPending = handshake->getPendingAuthKey();
authKeyPendingId = handshake->getPendingAuthKeyId();
break;
@ -1118,12 +1119,12 @@ NativeByteBuffer *Datacenter::createRequestsData(std::vector<std::unique_ptr<Net
}
if (LOGS_ENABLED) DEBUG_D("connection(%p, account%u, dc%u, type %d) send message (session: 0x%" PRIx64 ", seqno: %d, messageid: 0x%" PRIx64 "): %s(%p)", connection, instanceNum, datacenterId, connection->getConnectionType(), (uint64_t) connection->getSessionId(), networkMessage->message->seqno, (uint64_t) networkMessage->message->msg_id, typeid(*messageBody).name(), messageBody);
int64_t messageTime = (int64_t) (networkMessage->message->msg_id / 4294967296.0 * 1000);
auto messageTime = (int64_t) (networkMessage->message->msg_id / 4294967296.0 * 1000);
int64_t currentTime = ConnectionsManager::getInstance(instanceNum).getCurrentTimeMillis() + (int64_t) ConnectionsManager::getInstance(instanceNum).getTimeDifference() * 1000;
if (!pfsInit && (messageTime < currentTime - 30000 || messageTime > currentTime + 25000)) {
if (!pfsInit && (networkMessage->forceContainer || messageTime < currentTime - 30000 || messageTime > currentTime + 25000)) {
if (LOGS_ENABLED) DEBUG_D("wrap message in container");
TL_msg_container *messageContainer = new TL_msg_container();
auto messageContainer = new TL_msg_container();
messageContainer->messages.push_back(std::move(networkMessage->message));
messageId = ConnectionsManager::getInstance(instanceNum).generateMessageId();
@ -1136,7 +1137,7 @@ NativeByteBuffer *Datacenter::createRequestsData(std::vector<std::unique_ptr<Net
}
} else {
if (LOGS_ENABLED) DEBUG_D("start write messages to container");
TL_msg_container *messageContainer = new TL_msg_container();
auto messageContainer = new TL_msg_container();
size_t count = requests.size();
for (uint32_t a = 0; a < count; a++) {
NetworkMessage *networkMessage = requests[a].get();
@ -1400,7 +1401,7 @@ Connection *Datacenter::getConnectionByType(uint32_t connectionType, bool create
void Datacenter::onHandshakeComplete(Handshake *handshake, int64_t keyId, ByteArray *authKey, int32_t timeDifference) {
HandshakeType type = handshake->getType();
for (std::vector<std::unique_ptr<Handshake>>::iterator iter = handshakes.begin(); iter != handshakes.end(); iter++) {
for (auto iter = handshakes.begin(); iter != handshakes.end(); iter++) {
if (iter->get() == handshake) {
handshakes.erase(iter);
if (type == HandshakeTypePerm) {
@ -1431,17 +1432,17 @@ void Datacenter::exportAuthorization() {
return;
}
exportingAuthorization = true;
TL_auth_exportAuthorization *request = new TL_auth_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) {
TL_auth_exportedAuthorization *res = (TL_auth_exportedAuthorization *) response;
TL_auth_importAuthorization *request2 = new TL_auth_importAuthorization();
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;
@ -1497,7 +1504,7 @@ TL_help_configSimple *Datacenter::decodeSimpleConfig(NativeByteBuffer *buffer) {
BIO *keyBio = BIO_new(BIO_s_mem());
BIO_write(keyBio, public_key.c_str(), (int) public_key.length());
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, nullptr, nullptr, nullptr);
if (rsaKey == nullptr) {
if (rsaKey == nullptr) {
if (LOGS_ENABLED) DEBUG_E("Invalid rsa public key");

View File

@ -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;

View File

@ -17,9 +17,8 @@
#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 USE_OLD_KEYS
#define PFS_ENABLED 1
#define DEFAULT_DATACENTER_ID INT_MAX
#define DC_UPDATE_TIME 60 * 60
@ -28,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
@ -40,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;
@ -64,6 +59,7 @@ typedef struct NetworkMessage {
std::unique_ptr<TL_message> message;
bool invokeAfter = false;
bool needQuickAck = false;
bool forceContainer = false;
int32_t requestId;
} NetworkMessage;
@ -172,7 +168,9 @@ enum RequestFlag {
RequestFlagForceDownload = 32,
RequestFlagInvokeAfter = 64,
RequestFlagNeedQuickAck = 128,
RequestFlagUseUnboundKey = 256
RequestFlagUseUnboundKey = 256,
RequestFlagResendAfter = 512,
RequestFlagIgnoreFloodWait = 1024
};
inline std::string to_string_int32(int32_t value) {

View File

@ -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);
}

View File

@ -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

View File

@ -8,6 +8,7 @@
#include <stdlib.h>
#include <algorithm>
#include <memory>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/bn.h>
@ -55,19 +56,11 @@ void Handshake::beginHandshake(bool reconnect) {
connection->connect();
}
#ifdef USE_OLD_KEYS
TL_req_pq *request = new TL_req_pq();
request->nonce = std::unique_ptr<ByteArray>(new ByteArray(16));
auto request = new TL_req_pq_multi();
request->nonce = std::make_unique<ByteArray>(16);
RAND_bytes(request->nonce->bytes, 16);
authNonce = new ByteArray(request->nonce.get());
sendRequestData(request, true);
#else
TL_req_pq_multi *request = new TL_req_pq_multi();
request->nonce = std::unique_ptr<ByteArray>(new ByteArray(16));
RAND_bytes(request->nonce->bytes, 16);
authNonce = new ByteArray(request->nonce.get());
sendRequestData(request, true);
#endif
}
void Handshake::cleanupHandshake() {
@ -197,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;
@ -207,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;
@ -222,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;
@ -323,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;
@ -335,14 +333,14 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
}
handshakeState = 2;
TL_resPQ *result = (TL_resPQ *) message;
auto result = (TL_resPQ *) message;
if (authNonce->isEqualTo(result->nonce.get())) {
std::string key;
std::string key = "";
int64_t keyFingerprint = 0;
size_t count1 = result->server_public_key_fingerprints.size();
if (currentDatacenter->isCdnDatacenter) {
std::map<int32_t, uint64_t>::iterator iter = cdnPublicKeysFingerprints.find(currentDatacenter->datacenterId);
auto iter = cdnPublicKeysFingerprints.find(currentDatacenter->datacenterId);
if (iter != cdnPublicKeysFingerprints.end()) {
for (uint32_t a = 0; a < count1; a++) {
if ((uint64_t) result->server_public_key_fingerprints[a] == iter->second) {
@ -353,95 +351,35 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
}
} else {
if (serverPublicKeys.empty()) {
#ifdef USE_OLD_KEYS
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6\n"
"lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS\n"
"an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw\n"
"Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+\n"
"8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n\n"
"Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0xc3b42b026ce86b21LL);
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt\n"
"ksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru\n"
"vCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L\n"
"xI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi\n"
"XvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp\n"
"NTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0x9a996a1db11c729bLL);
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+\n"
"DyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB\n"
"1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s\n"
"g1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z\n"
"hRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f\n"
"x5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0xb05b2a6f70cdea78LL);
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa\n"
"xDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i\n"
"qAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc\n"
"/n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks\n"
"WV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t\n"
"UiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0x71e025b6c76033e3LL);
#endif
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAruw2yP/BCcsJliRoW5eBVBVle9dtjJw+OYED160Wybum9SXtBBLX\n"
"riwt4rROd9csv0t0OHCaTmRqBcQ0J8fxhN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/\n"
"j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvdl84Kd9ORYjDEAyFnEA7dD556OptgLQQ2\n"
"e2iVNq8NZLYTzLp5YpOdO1doK+ttrltggTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnS\n"
"Lj16yE5HvJQn0CNpRdENvRUXe6tBP78O39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wF\n"
"XGF710w9lwCGNbmNxNYhtIkdqfsEcwR5JwIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0xbc35f3509f7b7a5LL);
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAvfLHfYH2r9R70w8prHblWt/nDkh+XkgpflqQVcnAfSuTtO05lNPs\n"
"pQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOOKPi0OfJXoRVylFzAQG/j83u5K3kRLbae\n"
"7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ3TDS2pQOCtovG4eDl9wacrXOJTG2990V\n"
"jgnIKNA0UMoP+KF03qzryqIt3oTvZq03DyWdGK+AZjgBLaDKSnC6qD2cFY81UryR\n"
"WOab8zKkWAnhw2kFpcqhI0jdV5QaSCExvnsjVaX0Y1N0870931/5Jb9ICe4nweZ9\n"
"kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV/wIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0x15ae5fa8b5529542LL);
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAs/ditzm+mPND6xkhzwFIz6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGr\n"
"zqTDHkO30R8VeRM/Kz2f4nR05GIFiITl4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+\n"
"th6knSU0yLtNKuQVP6voMrnt9MV1X92LGZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvS\n"
"Uwwc+yi1/gGaybwlzZwqXYoPOhwMebzKUk0xW14htcJrRrq+PXXQbRzTMynseCoP\n"
"Ioke0dtCodbA3qQxQovE16q9zz4Otv2k4j63cz53J+mhkVWAeWxVGI0lltJmWtEY\n"
"K6er8VqqWot3nqmWMXogrgRLggv/NbbooQIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0xaeae98e13cd7f94fLL);
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q05shjg8/4p6047bn6/m8yPy1RBsvIyvuD\n"
"uGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xbnfxL5BXHplJhMtADXKM9bWB11PU1Eioc\n"
"3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvi\n"
"fRLJbY08/Gp66KpQvy7g8w7VB8wlgePexW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqe\n"
"Pji9NP3tJUFQjcECqcm0yV7/2d0t/pbCm+ZH1sadZspQCEPPrtbkQBlvHb4OLiIW\n"
"PGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6MAQIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0x5a181b2235057d98LL);
if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).testBackend) {
serverPublicKeys.emplace_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAyMEdY1aR+sCR3ZSJrtztKTKqigvO/vBfqACJLZtS7QMgCGXJ6XIR\n"
"yy7mx66W0/sOFa7/1mAZtEoIokDP3ShoqF4fVNb6XeqgQfaUHd8wJpDWHcR2OFwv\n"
"plUUI1PLTktZ9uW2WE23b+ixNwJjJGwBDJPQEQFBE+vfmH0JP503wr5INS1poWg/\n"
"j25sIWeYPHYeOrFp/eXaqhISP6G+q2IeTaWTXpwZj4LzXq5YOpk4bYEQ6mvRq7D1\n"
"aHWfYmlEGepfaYR8Q0YqvvhYtMte3ITnuSJs171+GDqpdKcSwHnd6FudwGO4pcCO\n"
"j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0xb25898df208d2603);
} else {
serverPublicKeys.emplace_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEA6LszBcC1LGzyr992NzE0ieY+BSaOW622Aa9Bd4ZHLl+TuFQ4lo4g\n"
"5nKaMBwK/BIb9xUfg0Q29/2mgIR6Zr9krM7HjuIcCzFvDtr+L0GQjae9H0pRB2OO\n"
"62cECs5HKhT5DZ98K33vmWiLowc621dQuwKWSQKjWf50XYFw42h21P2KXUGyp2y/\n"
"+aEyZ+uVgLLQbRA1dEjSDZ2iGRy12Mk5gpYc397aYp438fsJoHIgJ2lgMv5h7WY9\n"
"t6N/byY9Nw9p21Og3AoXSL2q/2IJ1WRUhebgAdGVMlV1fkuOQoEzR7EdpqtQD9Cs\n"
"5+bfo3Nhmcyvk5ftB0WkJ9z6bNZ7yxrP8wIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0xd09d1d85de64fd85);
}
}
size_t count2 = serverPublicKeysFingerprints.size();
for (uint32_t a = 0; a < count2; a++) {
for (uint32_t b = 0; b < count1; b++) {
if ((uint64_t) result->server_public_key_fingerprints[b] == serverPublicKeysFingerprints[a]) {
keyFingerprint = result->server_public_key_fingerprints[b];
key = serverPublicKeys[a];
for (uint32_t a = 0; a < count1; a++) {
for (uint32_t b = 0; b < count2; b++) {
if ((uint64_t) result->server_public_key_fingerprints[a] == serverPublicKeysFingerprints[b]) {
keyFingerprint = result->server_public_key_fingerprints[a];
key = serverPublicKeys[b];
break;
}
}
@ -457,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;
@ -478,15 +418,15 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
return;
}
TL_req_DH_params *request = new TL_req_DH_params();
request->nonce = std::unique_ptr<ByteArray>(new ByteArray(authNonce));
request->server_nonce = std::unique_ptr<ByteArray>(new ByteArray(authServerNonce));
request->p = std::unique_ptr<ByteArray>(new ByteArray(4));
auto request = new TL_req_DH_params();
request->nonce = std::make_unique<ByteArray>(new ByteArray(authNonce));
request->server_nonce = std::make_unique<ByteArray>(new ByteArray(authServerNonce));
request->p = std::make_unique<ByteArray>(new ByteArray(4));
request->p->bytes[3] = (uint8_t) p;
request->p->bytes[2] = (uint8_t) (p >> 8);
request->p->bytes[1] = (uint8_t) (p >> 16);
request->p->bytes[0] = (uint8_t) (p >> 24);
request->q = std::unique_ptr<ByteArray>(new ByteArray(4));
request->q = std::make_unique<ByteArray>(new ByteArray(4));
request->q->bytes[3] = (uint8_t) q;
request->q->bytes[2] = (uint8_t) (q >> 8);
request->q->bytes[1] = (uint8_t) (q >> 16);
@ -495,13 +435,13 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
TLObject *innerData;
if (handshakeType == HandshakeTypePerm) {
TL_p_q_inner_data_dc *tl_p_q_inner_data = new TL_p_q_inner_data_dc();
tl_p_q_inner_data->nonce = std::unique_ptr<ByteArray>(new ByteArray(authNonce));
tl_p_q_inner_data->server_nonce = std::unique_ptr<ByteArray>(new ByteArray(authServerNonce));
tl_p_q_inner_data->pq = std::unique_ptr<ByteArray>(new ByteArray(result->pq.get()));
tl_p_q_inner_data->p = std::unique_ptr<ByteArray>(new ByteArray(request->p.get()));
tl_p_q_inner_data->q = std::unique_ptr<ByteArray>(new ByteArray(request->q.get()));
tl_p_q_inner_data->new_nonce = std::unique_ptr<ByteArray>(new ByteArray(32));
auto tl_p_q_inner_data = new TL_p_q_inner_data_dc();
tl_p_q_inner_data->nonce = std::make_unique<ByteArray>(authNonce);
tl_p_q_inner_data->server_nonce = std::make_unique<ByteArray>(authServerNonce);
tl_p_q_inner_data->pq = std::make_unique<ByteArray>(new ByteArray(result->pq.get()));
tl_p_q_inner_data->p = std::make_unique<ByteArray>(new ByteArray(request->p.get()));
tl_p_q_inner_data->q = std::make_unique<ByteArray>(new ByteArray(request->q.get()));
tl_p_q_inner_data->new_nonce = std::make_unique<ByteArray>(new ByteArray(32));
if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).testBackend) {
tl_p_q_inner_data->dc = 10000 + currentDatacenter->datacenterId;
} else {
@ -511,13 +451,13 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
authNewNonce = new ByteArray(tl_p_q_inner_data->new_nonce.get());
innerData = tl_p_q_inner_data;
} else {
TL_p_q_inner_data_temp_dc *tl_p_q_inner_data_temp = new TL_p_q_inner_data_temp_dc();
tl_p_q_inner_data_temp->nonce = std::unique_ptr<ByteArray>(new ByteArray(authNonce));
tl_p_q_inner_data_temp->server_nonce = std::unique_ptr<ByteArray>(new ByteArray(authServerNonce));
tl_p_q_inner_data_temp->pq = std::unique_ptr<ByteArray>(new ByteArray(result->pq.get()));
tl_p_q_inner_data_temp->p = std::unique_ptr<ByteArray>(new ByteArray(request->p.get()));
tl_p_q_inner_data_temp->q = std::unique_ptr<ByteArray>(new ByteArray(request->q.get()));
tl_p_q_inner_data_temp->new_nonce = std::unique_ptr<ByteArray>(new ByteArray(32));
auto tl_p_q_inner_data_temp = new TL_p_q_inner_data_temp_dc();
tl_p_q_inner_data_temp->nonce = std::make_unique<ByteArray>(new ByteArray(authNonce));
tl_p_q_inner_data_temp->server_nonce = std::make_unique<ByteArray>(new ByteArray(authServerNonce));
tl_p_q_inner_data_temp->pq = std::make_unique<ByteArray>(new ByteArray(result->pq.get()));
tl_p_q_inner_data_temp->p = std::make_unique<ByteArray>(new ByteArray(request->p.get()));
tl_p_q_inner_data_temp->q = std::make_unique<ByteArray>(new ByteArray(request->q.get()));
tl_p_q_inner_data_temp->new_nonce = std::make_unique<ByteArray>(new ByteArray(32));
if (handshakeType == HandshakeTypeMediaTemp) {
if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).testBackend) {
tl_p_q_inner_data_temp->dc = -(10000 + currentDatacenter->datacenterId);
@ -538,32 +478,83 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
}
uint32_t innerDataSize = innerData->getObjectSize();
uint32_t additionalSize = innerDataSize + SHA_DIGEST_LENGTH < 255 ? 255 - (innerDataSize + SHA_DIGEST_LENGTH) : 0;
NativeByteBuffer *innerDataBuffer = BuffersStorage::getInstance().getFreeBuffer(innerDataSize + additionalSize + SHA_DIGEST_LENGTH);
innerDataBuffer->position(SHA_DIGEST_LENGTH);
if (innerDataSize > 144) {
if (LOGS_ENABLED) DEBUG_E("account%u dc%u handshake: inner data too large %d, type = %d", currentDatacenter->instanceNum, currentDatacenter->datacenterId, innerDataSize, handshakeType);
delete innerData;
beginHandshake(false);
return;
}
uint32_t keySize = 32;
uint32_t ivSize = 32;
uint32_t paddedDataSize = 192;
uint32_t encryptedDataSize = keySize + paddedDataSize + SHA256_DIGEST_LENGTH;
uint32_t additionalSize = innerDataSize < paddedDataSize ? paddedDataSize - innerDataSize : 0;
NativeByteBuffer *innerDataBuffer = BuffersStorage::getInstance().getFreeBuffer(encryptedDataSize + paddedDataSize + ivSize + SHA256_DIGEST_LENGTH + 256);
innerDataBuffer->position(encryptedDataSize);
innerData->serializeToStream(innerDataBuffer);
delete innerData;
SHA1(innerDataBuffer->bytes() + SHA_DIGEST_LENGTH, innerDataSize, innerDataBuffer->bytes());
if (additionalSize != 0) {
RAND_bytes(innerDataBuffer->bytes() + SHA_DIGEST_LENGTH + innerDataSize, additionalSize);
}
BIO *keyBio = BIO_new(BIO_s_mem());
BIO_write(keyBio, key.c_str(), (int) key.length());
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, nullptr, nullptr, nullptr);
BIO_free(keyBio);
while (true) {
RAND_bytes(innerDataBuffer->bytes() + encryptedDataSize + innerDataSize, additionalSize);
for (uint32_t i = 0; i < paddedDataSize; i++) {
innerDataBuffer->bytes()[keySize + i] = innerDataBuffer->bytes()[encryptedDataSize + paddedDataSize - i - 1];
}
RAND_bytes(innerDataBuffer->bytes(), keySize);
SHA256_CTX sha256Ctx;
SHA256_Init(&sha256Ctx);
SHA256_Update(&sha256Ctx, innerDataBuffer->bytes(), keySize);
SHA256_Update(&sha256Ctx, innerDataBuffer->bytes() + encryptedDataSize, paddedDataSize);
SHA256_Final(innerDataBuffer->bytes() + keySize + paddedDataSize, &sha256Ctx);
memset(innerDataBuffer->bytes() + encryptedDataSize + paddedDataSize, 0, ivSize);
Datacenter::aesIgeEncryption(innerDataBuffer->bytes() + keySize, innerDataBuffer->bytes(), innerDataBuffer->bytes() + encryptedDataSize + paddedDataSize, true, true, paddedDataSize + SHA256_DIGEST_LENGTH);
SHA256_Init(&sha256Ctx);
SHA256_Update(&sha256Ctx, innerDataBuffer->bytes() + keySize, paddedDataSize + SHA256_DIGEST_LENGTH);
SHA256_Final(innerDataBuffer->bytes() + encryptedDataSize + paddedDataSize + ivSize, &sha256Ctx);
for (uint32_t i = 0; i < keySize; i++) {
innerDataBuffer->bytes()[i] ^= innerDataBuffer->bytes()[encryptedDataSize + paddedDataSize + ivSize + i];
}
bool ok = false;
uint32_t offset = encryptedDataSize + paddedDataSize + ivSize + SHA256_DIGEST_LENGTH;
size_t resLen = BN_bn2bin(rsaKey->n, innerDataBuffer->bytes() + offset);
const auto shift = (256 - resLen);
for (auto i = 0; i != 256; ++i) {
const auto a = innerDataBuffer->bytes()[i];
const auto b = (i < shift) ? 0 : innerDataBuffer->bytes()[offset + i - shift];
if (a > b) {
break;
} else if (a < b) {
ok = true;
break;
}
}
if (ok) {
break;
}
}
if (bnContext == nullptr) {
bnContext = BN_CTX_new();
}
BIGNUM *a = BN_bin2bn(innerDataBuffer->bytes(), innerDataBuffer->limit(), NULL);
BIGNUM *a = BN_bin2bn(innerDataBuffer->bytes(), encryptedDataSize, nullptr);
BIGNUM *r = BN_new();
BN_mod_exp(r, a, rsaKey->e, rsaKey->n, bnContext);
uint32_t size = BN_num_bytes(r);
ByteArray *rsaEncryptedData = new ByteArray(size >= 256 ? size : 256);
size_t resLen = BN_bn2bin(r, rsaEncryptedData->bytes);
if (256 - resLen > 0) {
memset(rsaEncryptedData->bytes + resLen, 0, 256 - resLen);
auto rsaEncryptedData = new ByteArray(size >= 256 ? size : 256);
BN_bn2bin(r, rsaEncryptedData->bytes + (size < 256 ? (256 - size) : 0));
if (256 - size > 0) {
memset(rsaEncryptedData->bytes, 0, 256 - size);
}
BN_free(a);
BN_free(r);
@ -864,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)) {
@ -911,7 +902,8 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
}
void Handshake::sendAckRequest(int64_t messageId) {
/*TL_msgs_ack *msgsAck = new TL_msgs_ack();
/*
auto msgsAck = new TL_msgs_ack();
msgsAck->msg_ids.push_back(messageId);
sendRequestData(msgsAck, false);*/
}
@ -923,10 +915,10 @@ TLObject *Handshake::getCurrentHandshakeRequest() {
void Handshake::saveCdnConfigInternal(NativeByteBuffer *buffer) {
buffer->writeInt32(1);
buffer->writeInt32((int32_t) cdnPublicKeys.size());
for (std::map<int32_t, std::string>::iterator iter = cdnPublicKeys.begin(); iter != cdnPublicKeys.end(); iter++) {
buffer->writeInt32(iter->first);
buffer->writeString(iter->second);
buffer->writeInt64(cdnPublicKeysFingerprints[iter->first]);
for (auto & cdnPublicKey : cdnPublicKeys) {
buffer->writeInt32(cdnPublicKey.first);
buffer->writeString(cdnPublicKey.second);
buffer->writeInt64(cdnPublicKeysFingerprints[cdnPublicKey.first]);
}
}
@ -934,7 +926,7 @@ void Handshake::saveCdnConfig(Datacenter *datacenter) {
if (cdnConfig == nullptr) {
cdnConfig = new Config(datacenter->instanceNum, "cdnkeys.dat");
}
thread_local static NativeByteBuffer *sizeCalculator = new NativeByteBuffer(true);
thread_local static auto sizeCalculator = new NativeByteBuffer(true);
sizeCalculator->clearCapacity();
saveCdnConfigInternal(sizeCalculator);
NativeByteBuffer *buffer = BuffersStorage::getInstance().getFreeBuffer(sizeCalculator->capacity());
@ -978,11 +970,11 @@ void Handshake::loadCdnConfig(Datacenter *datacenter) {
}
}
loadingCdnKeys = true;
TL_help_getCdnConfig *request = new TL_help_getCdnConfig();
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) {
TL_cdnConfig *config = (TL_cdnConfig *) response;
auto config = (TL_cdnConfig *) response;
size_t count = config->public_keys.size();
BIO *keyBio = BIO_new(BIO_s_mem());
NativeByteBuffer *buffer = BuffersStorage::getInstance().getFreeBuffer(1024);
@ -992,7 +984,7 @@ void Handshake::loadCdnConfig(Datacenter *datacenter) {
cdnPublicKeys[publicKey->dc_id] = publicKey->public_key;
BIO_write(keyBio, publicKey->public_key.c_str(), (int) publicKey->public_key.length());
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, nullptr, nullptr, nullptr);
int nBytes = BN_num_bytes(rsaKey->n);
int eBytes = BN_num_bytes(rsaKey->e);

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -47,6 +47,7 @@ public:
int64_t startTimeMillis = 0;
int32_t minStartTime = 0;
int32_t lastResendTime = 0;
bool isResending = false;
int32_t instanceNum = 0;
uint32_t serverFailureCount = 0;
TLObject *rawRequest;
@ -58,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();

View File

@ -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.

View File

@ -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__

View 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_

View 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

View File

@ -0,0 +1,65 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#include "common/basictypes.h"
#include <stddef.h>
namespace google_breakpad {
// CrashGenerationClient is an interface for implementing out-of-process crash
// dumping. The default implementation, accessed via the TryCreate() factory,
// works in conjunction with the CrashGenerationServer to generate a minidump
// via a remote process.
class CrashGenerationClient {
public:
CrashGenerationClient() {}
virtual ~CrashGenerationClient() {}
// Request the crash server to generate a dump. |blob| is an opaque
// CrashContext pointer from exception_handler.h.
// Returns true if the dump was successful; false otherwise.
virtual bool RequestDump(const void* blob, size_t blob_size) = 0;
// Returns a new CrashGenerationClient if |server_fd| is valid and
// connects to a CrashGenerationServer. Otherwise, return NULL.
// The returned CrashGenerationClient* is owned by the caller of
// this function.
static CrashGenerationClient* TryCreate(int server_fd);
private:
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_

View 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

View 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_

View 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

View 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

View 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_

View 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

View 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

View 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

View 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 = &regs;
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

View 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_

View 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

View 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

View 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, &current_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

View 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_

File diff suppressed because it is too large Load Diff

View 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

View 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_

View File

@ -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

View File

@ -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_

View 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

View 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_

View File

@ -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

View 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_

View 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());
}

View 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_

View File

@ -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);
}

View 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_

View 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));
}

View 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

View 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_

View File

@ -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);
}
}

View 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

View 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_

View File

@ -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;
}

View 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, &regs) == -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

View 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_

View File

@ -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));
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More