mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-04-14 22:51:50 +03:00
Compare commits
1496 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81034ce1f1 | ||
|
|
b8646f4db3 | ||
|
|
6515df8cec | ||
|
|
f265645d02 | ||
|
|
db7d99703d | ||
|
|
6281482c39 | ||
|
|
3cc56d325c | ||
|
|
a47fa33b5a | ||
|
|
b507644cb0 | ||
|
|
3ac9f96158 | ||
|
|
5904a42187 | ||
|
|
2fceece256 | ||
|
|
234404d324 | ||
|
|
675ab079e7 | ||
|
|
3f6e63e86a | ||
|
|
dd30c91eaa | ||
|
|
3a8bee81f1 | ||
|
|
c2d57040f0 | ||
|
|
eab2b29736 | ||
|
|
a0f2bdf91d | ||
|
|
87f9938de5 | ||
|
|
513778bcb1 | ||
|
|
176ea0a801 | ||
|
|
19bbefe136 | ||
|
|
a87c518bab | ||
|
|
d758349ca4 | ||
|
|
1d018d8fda | ||
|
|
86b7219213 | ||
|
|
ad726adf75 | ||
|
|
a056c20d67 | ||
|
|
a75655badf | ||
|
|
0ac9380cf1 | ||
|
|
1cc563f531 | ||
|
|
c73ab1c2ff | ||
|
|
12b33ed8f1 | ||
|
|
79cc7dcc82 | ||
|
|
a10ef3e1f1 | ||
|
|
0fa27fd28f | ||
|
|
34ace9169b | ||
|
|
4d0ffa075b | ||
|
|
d4908fe44a | ||
|
|
c51abba131 | ||
|
|
5f353dc283 | ||
|
|
365747bc8d | ||
|
|
6016d86c18 | ||
|
|
c7690e92da | ||
|
|
441e840df7 | ||
|
|
0988583d7c | ||
|
|
8a06b086f3 | ||
|
|
355774270d | ||
|
|
47aeff1d08 | ||
|
|
6d4611197e | ||
|
|
3678a57c10 | ||
|
|
2cfa41913e | ||
|
|
87555dfbb2 | ||
|
|
f714cd9249 | ||
|
|
5e49c28ef0 | ||
|
|
7c40be249c | ||
|
|
707aab0c95 | ||
|
|
a199278f37 | ||
|
|
ace437d26a | ||
|
|
2688047b56 | ||
|
|
de886e05d2 | ||
|
|
755735a6bf | ||
|
|
d70882a05d | ||
|
|
67435187d1 | ||
|
|
261591f123 | ||
|
|
56cdce8592 | ||
|
|
daf44173c5 | ||
|
|
ce02945070 | ||
|
|
c51949ea3e | ||
|
|
5a634a8d0a | ||
|
|
5d5f76ee01 | ||
|
|
9442300cc3 | ||
|
|
2a66c0be5a | ||
|
|
38b12427b7 | ||
|
|
9fdc1160c5 | ||
|
|
48b4ad60a7 | ||
|
|
2114349a4e | ||
|
|
ced8b3cffb | ||
|
|
5e98585b37 | ||
|
|
e4fa33148a | ||
|
|
d73de95f72 | ||
|
|
9528a2e2dd | ||
|
|
a156e997d7 | ||
|
|
395e63bf7e | ||
|
|
4246475b44 | ||
|
|
f87bbab22c | ||
|
|
711fff750c | ||
|
|
5847516692 | ||
|
|
6200e8987f | ||
|
|
a952a3b8b0 | ||
|
|
e350c71571 | ||
|
|
95fc091b0f | ||
|
|
c5547f9e64 | ||
|
|
015b017973 | ||
|
|
e6864c6075 | ||
|
|
1972241cd2 | ||
|
|
27d7960cf9 | ||
|
|
edaab8b3ad | ||
|
|
4531411abe | ||
|
|
1818170c8d | ||
|
|
fd60645260 | ||
|
|
5e460bfea2 | ||
|
|
9169e9272a | ||
|
|
f19a569216 | ||
|
|
b6338c4ff6 | ||
|
|
554185356b | ||
|
|
3cee771cfa | ||
|
|
3c14707b01 | ||
|
|
37bf846cc3 | ||
|
|
8347f1045a | ||
|
|
01e9ecbeb2 | ||
|
|
852da1be15 | ||
|
|
1956a54a43 | ||
|
|
0dfb4a5a1a | ||
|
|
cfa90dfd80 | ||
|
|
a3910b9802 | ||
|
|
c23a6bfdf6 | ||
|
|
c20a63a765 | ||
|
|
f81fb92a89 | ||
|
|
f19f49ef3e | ||
|
|
80e9001af3 | ||
|
|
3688dfb5c3 | ||
|
|
a4defdb854 | ||
|
|
e1a77ec558 | ||
|
|
86bbabac32 | ||
|
|
81f35e0b55 | ||
|
|
773b5809f9 | ||
|
|
ad108d50f1 | ||
|
|
22440a0207 | ||
|
|
70f019cd3a | ||
|
|
6035d4a8d3 | ||
|
|
c067a55c79 | ||
|
|
ac279d7e71 | ||
|
|
f55e0c3f5c | ||
|
|
499f306859 | ||
|
|
421b17a622 | ||
|
|
c17bf8b368 | ||
|
|
34c823f147 | ||
|
|
3bc89cfeca | ||
|
|
1abeae9ebd | ||
|
|
257e64b968 | ||
|
|
ef8e512e29 | ||
|
|
607b866035 | ||
|
|
20cc983314 | ||
|
|
52fa9577ba | ||
|
|
17881ebbfd | ||
|
|
4b88bddbca | ||
|
|
d8486b2653 | ||
|
|
587676fee8 | ||
|
|
a17385a882 | ||
|
|
6786934280 | ||
|
|
46690c9ec0 | ||
|
|
6092c980a6 | ||
|
|
3820e38dc1 | ||
|
|
0ce13c6fb5 | ||
|
|
a361e886e2 | ||
|
|
b82333fdec | ||
|
|
b9758afff0 | ||
|
|
2a693b83d2 | ||
|
|
6d625d5e5e | ||
|
|
397827a27d | ||
|
|
02251c0070 | ||
|
|
8c2b8bcf24 | ||
|
|
edc1576f03 | ||
|
|
3a0d9cdadb | ||
|
|
1c900088c3 | ||
|
|
4f4fd42447 | ||
|
|
6cc42173cb | ||
|
|
44db479fad | ||
|
|
0181aaa495 | ||
|
|
734f29ce56 | ||
|
|
de5606d0d8 | ||
|
|
1960536b61 | ||
|
|
3eb7a4b53d | ||
|
|
661fb1e672 | ||
|
|
db4f0e7182 | ||
|
|
8c2e15d1a5 | ||
|
|
60f472f367 | ||
|
|
323ed2e3a8 | ||
|
|
c1a3ca3755 | ||
|
|
3383b98f1b | ||
|
|
cd05b19f10 | ||
|
|
baa5a90cc6 | ||
|
|
7c99686165 | ||
|
|
0c88be9e0a | ||
|
|
e2c9f3a9ce | ||
|
|
14d5dc136a | ||
|
|
f68effe4ac | ||
|
|
9e123a833c | ||
|
|
e29ac61987 | ||
|
|
bd0a5b0f3b | ||
|
|
5b72ac098a | ||
|
|
8c54637f8c | ||
|
|
c7ccb8d7e9 | ||
|
|
aaa29003ab | ||
|
|
143f458188 | ||
|
|
0a9f51d0d8 | ||
|
|
a25b9b8ba9 | ||
|
|
8fefabd3a4 | ||
|
|
47c9bcd402 | ||
|
|
48f66cf4a2 | ||
|
|
8dc97b1108 | ||
|
|
bc32ddff2d | ||
|
|
b66f689764 | ||
|
|
c893fcd169 | ||
|
|
a1fcbebb18 | ||
|
|
8477ec9562 | ||
|
|
21bcc0a8d4 | ||
|
|
867c6dd7dc | ||
|
|
91a6d230db | ||
|
|
90c627edb7 | ||
|
|
f9c0b5f7f8 | ||
|
|
fc615739cb | ||
|
|
44d91cf243 | ||
|
|
6e56848850 | ||
|
|
c085530c71 | ||
|
|
70c94d7474 | ||
|
|
86f4851f5d | ||
|
|
5afff2e44e | ||
|
|
8d8379da44 | ||
|
|
47d69b4eab | ||
|
|
fa451de17f | ||
|
|
630434bb0a | ||
|
|
4b555c11a5 | ||
|
|
11038ff762 | ||
|
|
83b075789b | ||
|
|
5081c16bb4 | ||
|
|
38056fea64 | ||
|
|
268e8ee880 | ||
|
|
b2e59a96e1 | ||
|
|
92aa52c062 | ||
|
|
10d713151d | ||
|
|
1978e5cdac | ||
|
|
ed9b00a96b | ||
|
|
373884ab48 | ||
|
|
1aba4f41a3 | ||
|
|
6d181bc1b7 | ||
|
|
f96010b7fa | ||
|
|
a2c5267409 | ||
|
|
b1792c80d2 | ||
|
|
05160258df | ||
|
|
3a6296e1ef | ||
|
|
d284aad027 | ||
|
|
dfb3260b97 | ||
|
|
f6fe6abdcb | ||
|
|
eda05b3994 | ||
|
|
e4817c8d89 | ||
|
|
3025b021b9 | ||
|
|
e2cd27132a | ||
|
|
756d4df2fd | ||
|
|
04d1a87b78 | ||
|
|
d88fa71bbd | ||
|
|
6fb3b6a8e4 | ||
|
|
8a22d10b83 | ||
|
|
867eedfc58 | ||
|
|
630f7de952 | ||
|
|
6b197fdd46 | ||
|
|
36becb1302 | ||
|
|
005f20aa7f | ||
|
|
7a9e4c9073 | ||
|
|
72cfdce718 | ||
|
|
a442d9b895 | ||
|
|
fbca96c433 | ||
|
|
7d9eceaf38 | ||
|
|
ce8ce99a4a | ||
|
|
ed7e6fe71a | ||
|
|
7d563a8f81 | ||
|
|
b71da25b8a | ||
|
|
87c56c8df8 | ||
|
|
da66aa391f | ||
|
|
d2c9ed3d1e | ||
|
|
254c4847e8 | ||
|
|
4f50f782fa | ||
|
|
3aae792b10 | ||
|
|
424dd61d57 | ||
|
|
120abd703a | ||
|
|
162ff8365d | ||
|
|
07a2eab3ed | ||
|
|
ea5b7bea31 | ||
|
|
6816b23862 | ||
|
|
62648c88e5 | ||
|
|
b01d496646 | ||
|
|
9ba1e1cb37 | ||
|
|
8ff7e7d6c3 | ||
|
|
3e82f357bb | ||
|
|
4827bb17bd | ||
|
|
1431153695 | ||
|
|
7e54dd1ddb | ||
|
|
c49c17f128 | ||
|
|
cdb2c0e02f | ||
|
|
41e0b857be | ||
|
|
856db56f6e | ||
|
|
36ca0c1b7d | ||
|
|
314c073a38 | ||
|
|
65d3b5989b | ||
|
|
1d9e9c2ed6 | ||
|
|
5711dc31d8 | ||
|
|
589c63b424 | ||
|
|
e249d1a2a1 | ||
|
|
602edd7566 | ||
|
|
ebd7e99f5c | ||
|
|
5a858c64d6 | ||
|
|
e1338703ef | ||
|
|
d131331310 | ||
|
|
46e464a26b | ||
|
|
f2e00d2fd3 | ||
|
|
05385191d4 | ||
|
|
bb0333e745 | ||
|
|
210f0d0b2b | ||
|
|
90176f8a87 | ||
|
|
86eb49b478 | ||
|
|
5832ef6589 | ||
|
|
6d4aa33753 | ||
|
|
c1d3ad4674 | ||
|
|
d59e30cbc9 | ||
|
|
d577e9b588 | ||
|
|
a2259f9fa6 | ||
|
|
9c32689e57 | ||
|
|
4e6f1e9208 | ||
|
|
3e2ba7a651 | ||
|
|
0288126d9c | ||
|
|
d4a2b8bab1 | ||
|
|
94ace05832 | ||
|
|
6ea8a7e928 | ||
|
|
5bac384970 | ||
|
|
019cccc293 | ||
|
|
dc0a184f8d | ||
|
|
12311fe6c3 | ||
|
|
70344a2d38 | ||
|
|
6841110bd6 | ||
|
|
f2b28906e6 | ||
|
|
fc680128e0 | ||
|
|
521970fb2e | ||
|
|
5f64ad60cd | ||
|
|
434a68e221 | ||
|
|
e62aa478c7 | ||
|
|
ce0b7ab6c8 | ||
|
|
6cab460a45 | ||
|
|
5266152d79 | ||
|
|
be6da4f663 | ||
|
|
543e2d61e6 | ||
|
|
31e01a98f1 | ||
|
|
8b64be3441 | ||
|
|
8e7353a19b | ||
|
|
45249cf5a9 | ||
|
|
aba1645f2d | ||
|
|
d503d72129 | ||
|
|
71bc1a3d91 | ||
|
|
f743690739 | ||
|
|
4edea8eb8e | ||
|
|
09e4b38fb1 | ||
|
|
e8b28908de | ||
|
|
4422f88d17 | ||
|
|
c7805f1eb5 | ||
|
|
b6125120ac | ||
|
|
97b313c7d4 | ||
|
|
5fd55837bb | ||
|
|
8580c65f81 | ||
|
|
a74acb57e8 | ||
|
|
e8f9f13811 | ||
|
|
06374d2a6a | ||
|
|
14ad8205bf | ||
|
|
481bbfc990 | ||
|
|
143e9c4a2f | ||
|
|
be65438f20 | ||
|
|
1897f185d2 | ||
|
|
b04e7666f2 | ||
|
|
4c95c953e2 | ||
|
|
3de0c24859 | ||
|
|
c0c9783ec9 | ||
|
|
c9ac1f4701 | ||
|
|
ba19d2cb78 | ||
|
|
a0734fd6ee | ||
|
|
56ddbea270 | ||
|
|
ce29b4c3d9 | ||
|
|
42daa1ac44 | ||
|
|
36366f3c4c | ||
|
|
adc70c0511 | ||
|
|
4e12d21c8d | ||
|
|
58478412be | ||
|
|
a1c7d9c046 | ||
|
|
41a859ef73 | ||
|
|
cb578bbe01 | ||
|
|
a9215bf18a | ||
|
|
3713932836 | ||
|
|
4fc5c4fbac | ||
|
|
b950934916 | ||
|
|
df8f7d10af | ||
|
|
df7ad8a9b6 | ||
|
|
70e3735f3a | ||
|
|
5b1f2cc5d7 | ||
|
|
cd5aaf308a | ||
|
|
70d4102f48 | ||
|
|
54eaed1d8b | ||
|
|
304c919829 | ||
|
|
8cb814629a | ||
|
|
66c889500a | ||
|
|
a7d73dd4c9 | ||
|
|
254b011915 | ||
|
|
f5e840bbf0 | ||
|
|
ceca07d2ca | ||
|
|
391bad4b95 | ||
|
|
9a242f16d9 | ||
|
|
0e29ad4efa | ||
|
|
5841b6dbe7 | ||
|
|
ed5fc14b28 | ||
|
|
25517b852e | ||
|
|
8a49b62e78 | ||
|
|
fdb6c10162 | ||
|
|
a93931537e | ||
|
|
eaaa368bab | ||
|
|
5bf03f8ce5 | ||
|
|
52631c90f6 | ||
|
|
7ae0f15c59 | ||
|
|
eb65d1b078 | ||
|
|
78b58379c8 | ||
|
|
ca709c3139 | ||
|
|
063d134aeb | ||
|
|
a4e81221cc | ||
|
|
20f9802e4f | ||
|
|
8c59c44ffa | ||
|
|
efc539c040 | ||
|
|
002f0e9397 | ||
|
|
01a293fc08 | ||
|
|
b798fabdf7 | ||
|
|
eafd2ac39f | ||
|
|
36a09ba2c7 | ||
|
|
640c3c72e6 | ||
|
|
f15d8f3b41 | ||
|
|
eb196815d6 | ||
|
|
011449f17b | ||
|
|
8b49eb132e | ||
|
|
ddb170b1d9 | ||
|
|
648b3b9f76 | ||
|
|
d66162e032 | ||
|
|
c9946fa7e6 | ||
|
|
89fe8ee6bf | ||
|
|
6230cc88b6 | ||
|
|
61978bbe69 | ||
|
|
c91e62dd37 | ||
|
|
18510020e7 | ||
|
|
f509703af5 | ||
|
|
067c2da074 | ||
|
|
d660683d3d | ||
|
|
eabe889162 | ||
|
|
dfdd7562f5 | ||
|
|
01d61a3c6f | ||
|
|
8b34a788b5 | ||
|
|
e491cef9ab | ||
|
|
b75822bc6e | ||
|
|
06aac61c4b | ||
|
|
d038160f3b | ||
|
|
bd70d8fc0f | ||
|
|
e491df1d2f | ||
|
|
60b9637cc0 | ||
|
|
837b37c4ce | ||
|
|
310af725b0 | ||
|
|
cafe9a3158 | ||
|
|
bb5052ce90 | ||
|
|
9015e129bd | ||
|
|
d90655390f | ||
|
|
800ce49c19 | ||
|
|
f56f5b9930 | ||
|
|
2c70e8d351 | ||
|
|
0f6da1257d | ||
|
|
34b00f8969 | ||
|
|
62f9c54d2a | ||
|
|
d9bbf539ff | ||
|
|
7dcf77809c | ||
|
|
af6ee27c0d | ||
|
|
113e8e68e1 | ||
|
|
3b3257a709 | ||
|
|
cdabe908d0 | ||
|
|
400c59895a | ||
|
|
8b81d3f214 | ||
|
|
6bdb4f5ab0 | ||
|
|
37342a4d32 | ||
|
|
6cb585b13a | ||
|
|
b6a7a535b3 | ||
|
|
4d56aaeca5 | ||
|
|
26f5257b88 | ||
|
|
2159615419 | ||
|
|
8daac7958f | ||
|
|
c9ebff0fd6 | ||
|
|
912324a1ac | ||
|
|
cf9724531a | ||
|
|
ab0f1604b4 | ||
|
|
11b6db7448 | ||
|
|
83f3294027 | ||
|
|
3c4b717ffc | ||
|
|
deb8e62a83 | ||
|
|
7bb05e04be | ||
|
|
a9031a0970 | ||
|
|
f7d46b8119 | ||
|
|
523cfa55c5 | ||
|
|
6e848a005e | ||
|
|
8229cc77c5 | ||
|
|
97da57c13a | ||
|
|
b8b8027f19 | ||
|
|
26140dd246 | ||
|
|
e5062e9fb9 | ||
|
|
8b24cb8fdf | ||
|
|
c01a885e94 | ||
|
|
2c625d5cd9 | ||
|
|
9d02bdc883 | ||
|
|
5884a076fb | ||
|
|
6a01600712 | ||
|
|
f58064b932 | ||
|
|
27f71242b7 | ||
|
|
6f41ba55ee | ||
|
|
dae24589bc | ||
|
|
40d53e007c | ||
|
|
dcb7b83fac | ||
|
|
252e0942d0 | ||
|
|
dc0a4b8b2f | ||
|
|
0170dd198a | ||
|
|
08a4cc0969 | ||
|
|
92a1e38f52 | ||
|
|
d93eef2f40 | ||
|
|
e09eac1d4e | ||
|
|
c88fe355e6 | ||
|
|
aaea4fd1e6 | ||
|
|
4b633b9a81 | ||
|
|
6630c59896 | ||
|
|
113938b6f4 | ||
|
|
1d4a7666d5 | ||
|
|
583284f2d9 | ||
|
|
ace329d11b | ||
|
|
47d8a7e6b0 | ||
|
|
41fd56605e | ||
|
|
347523517b | ||
|
|
9c42ed2d14 | ||
|
|
d202218e86 | ||
|
|
de033f56c0 | ||
|
|
4452a4812f | ||
|
|
0689448b1e | ||
|
|
4fb93a18ee | ||
|
|
2381efab57 | ||
|
|
2c0f4c2ac3 | ||
|
|
36c6bfb963 | ||
|
|
11beab38bc | ||
|
|
08089589f7 | ||
|
|
5417938215 | ||
|
|
b2c08ef2e6 | ||
|
|
aea91b8c33 | ||
|
|
1f688490e1 | ||
|
|
4f7cb3a413 | ||
|
|
12cd13cd41 | ||
|
|
304cdbb132 | ||
|
|
9b523c6c15 | ||
|
|
ce68f326b0 | ||
|
|
7dc77527ba | ||
|
|
03d95cba88 | ||
|
|
3093d9455e | ||
|
|
7c964b0352 | ||
|
|
add636596a | ||
|
|
49b7d7f0a4 | ||
|
|
1784939688 | ||
|
|
9ea235f8fe | ||
|
|
4d8c22f9a5 | ||
|
|
70d1541c5b | ||
|
|
862219e461 | ||
|
|
a137a68252 | ||
|
|
2ae1ef7dbd | ||
|
|
61afb6a405 | ||
|
|
9193ea2248 | ||
|
|
3913077146 | ||
|
|
11127240ca | ||
|
|
22be724af4 | ||
|
|
73ca4b8ef8 | ||
|
|
0f6c420f83 | ||
|
|
6bddb92ad6 | ||
|
|
154aa5fcc1 | ||
|
|
271a676dcd | ||
|
|
d21d5b46b6 | ||
|
|
fb327368db | ||
|
|
ce9386370a | ||
|
|
cdae6706a6 | ||
|
|
480f3b11cd | ||
|
|
bf448d7a5a | ||
|
|
1944ebbe7f | ||
|
|
f47b4c2cd8 | ||
|
|
4b8870c7db | ||
|
|
cde7097eca | ||
|
|
a11be50332 | ||
|
|
8c5e5f50a2 | ||
|
|
041145c272 | ||
|
|
f3b2668b32 | ||
|
|
edbfe6912c | ||
|
|
79f81a3732 | ||
|
|
32dd153796 | ||
|
|
4790db15ed | ||
|
|
b3df80bc79 | ||
|
|
bdb7307ff2 | ||
|
|
caef4c2868 | ||
|
|
56e85c0e47 | ||
|
|
dc886e5608 | ||
|
|
9fd9c876bb | ||
|
|
da63f23e68 | ||
|
|
0ea3d6307c | ||
|
|
bf64557ed6 | ||
|
|
99fc0717e6 | ||
|
|
061cabb712 | ||
|
|
d3e5ea03c5 | ||
|
|
68a1666e91 | ||
|
|
be0d7a53f3 | ||
|
|
55e0f60ca1 | ||
|
|
94cd9444c5 | ||
|
|
b25ee5d88e | ||
|
|
746ea3de6f | ||
|
|
30b9e8162b | ||
|
|
70692cfb13 | ||
|
|
9b75808be1 | ||
|
|
2ae966222f | ||
|
|
ff4086aa6b | ||
|
|
3624dd42ff | ||
|
|
20140629b4 | ||
|
|
c259323ab3 | ||
|
|
8edfc5b170 | ||
|
|
3967329813 | ||
|
|
2fcbd18115 | ||
|
|
4c46e11365 | ||
|
|
229994a204 | ||
|
|
31a629c3de | ||
|
|
9f9247a62e | ||
|
|
181ba7fd4d | ||
|
|
c007c537ff | ||
|
|
35a8552605 | ||
|
|
f6699803e2 | ||
|
|
a943172b73 | ||
|
|
2e3104ba07 | ||
|
|
a011c4c22d | ||
|
|
14d689c0f9 | ||
|
|
9f71b5779b | ||
|
|
1f1a0231ed | ||
|
|
4352cbc21c | ||
|
|
54f3351f1f | ||
|
|
40fa4d29d3 | ||
|
|
afa489c3c5 | ||
|
|
f9bb8dedef | ||
|
|
a9fa2defdb | ||
|
|
5d8e70ab26 | ||
|
|
83cad746ae | ||
|
|
526180b76d | ||
|
|
b35ac00d58 | ||
|
|
8a56d6b636 | ||
|
|
22d62d8cbd | ||
|
|
6c5a3a24dd | ||
|
|
ea013d8fa4 | ||
|
|
74bd63b203 | ||
|
|
4557c0a67d | ||
|
|
006dd0414e | ||
|
|
f2e1a5be77 | ||
|
|
6ab181d2b7 | ||
|
|
3a627b9674 | ||
|
|
91e006c4c2 | ||
|
|
063a767ffe | ||
|
|
4e3fe218e9 | ||
|
|
26c1dc5a3a | ||
|
|
96a59c3bb5 | ||
|
|
986cbe4881 | ||
|
|
1e3b8636ff | ||
|
|
e82771807e | ||
|
|
0dfdd31e0f | ||
|
|
9522ae41d6 | ||
|
|
a559caf74a | ||
|
|
f51948d9e1 | ||
|
|
54c94c1679 | ||
|
|
e6c057ad35 | ||
|
|
734e72ce8f | ||
|
|
d9f7e6c668 | ||
|
|
3ed0b4e8a3 | ||
|
|
fffcefed33 | ||
|
|
f7cf23aa4d | ||
|
|
f9299ca572 | ||
|
|
0971e1e4e3 | ||
|
|
5228d869ee | ||
|
|
089f8fa442 | ||
|
|
ca30b5db2b | ||
|
|
4a15008cfb | ||
|
|
43af63fff4 | ||
|
|
63677dde63 | ||
|
|
c1b2a77933 | ||
|
|
d0a991d47b | ||
|
|
d438296b1f | ||
|
|
ecd39418ac | ||
|
|
99c2d6c232 | ||
|
|
520b75fa2d | ||
|
|
92e189be8b | ||
|
|
d96e4525ad | ||
|
|
ac480136d7 | ||
|
|
be5e49f4fa | ||
|
|
4a65f34930 | ||
|
|
566c4a8594 | ||
|
|
9545c2cd36 | ||
|
|
cf2549a149 | ||
|
|
4ca3d91e96 | ||
|
|
b4c37a6e81 | ||
|
|
95f0a77fde | ||
|
|
b3c5690b7e | ||
|
|
589638182a | ||
|
|
03a6047111 | ||
|
|
c9757d9e3b | ||
|
|
1b3ee75667 | ||
|
|
27ef02ca9a | ||
|
|
d2d941017b | ||
|
|
180b843159 | ||
|
|
ef6d51ed44 | ||
|
|
bf72188f80 | ||
|
|
ea32060f9c | ||
|
|
d16849c91d | ||
|
|
634ec6f50a | ||
|
|
6599651aee | ||
|
|
ea51e97bb8 | ||
|
|
1964b08394 | ||
|
|
534504d4a7 | ||
|
|
484f04733e | ||
|
|
bf025d2ec8 | ||
|
|
1c7da33317 | ||
|
|
c8209150f9 | ||
|
|
5ba861715a | ||
|
|
4ef5b8b4df | ||
|
|
5e41ff9b74 | ||
|
|
3de19ba401 | ||
|
|
be9548f2be | ||
|
|
a9aa6f6d0f | ||
|
|
b971f7c4dd | ||
|
|
21b70cb540 | ||
|
|
1ed7ec369f | ||
|
|
2a6ba121b5 | ||
|
|
9e6aa77ab9 | ||
|
|
0513047170 | ||
|
|
bdb60a8053 | ||
|
|
025d8c37c9 | ||
|
|
f6bbfc1e96 | ||
|
|
259c5e3e8f | ||
|
|
018b162d67 | ||
|
|
ed99d300b9 | ||
|
|
e034500698 | ||
|
|
7ad2f78663 | ||
|
|
40cf71a06d | ||
|
|
36ebb5abe3 | ||
|
|
1541ffc765 | ||
|
|
d243b4ec48 | ||
|
|
09eda2c9b6 | ||
|
|
b549389e4a | ||
|
|
202f01d4f8 | ||
|
|
866231fc61 | ||
|
|
20f2479ed7 | ||
|
|
8efcdc3f98 | ||
|
|
5e90fd006e | ||
|
|
c57494879f | ||
|
|
ffe552223c | ||
|
|
131b1b5338 | ||
|
|
b399463fba | ||
|
|
b0ffa39cac | ||
|
|
753bbf1849 | ||
|
|
7b187360e9 | ||
|
|
32d4673221 | ||
|
|
38867c5c17 | ||
|
|
ab274a23b9 | ||
|
|
9e18ae639f | ||
|
|
8f9e958e1e | ||
|
|
743021b63f | ||
|
|
eaed1e39be | ||
|
|
53084cc5c2 | ||
|
|
60993697d8 | ||
|
|
81c2f841e5 | ||
|
|
e032a1a1de | ||
|
|
f6cf5eb388 | ||
|
|
978f830ee3 | ||
|
|
c6f59e9bb4 | ||
|
|
f805468957 | ||
|
|
49e5c2fe7d | ||
|
|
2bb8060d57 | ||
|
|
f28cc2bc87 | ||
|
|
ddb8dc4ad0 | ||
|
|
ceee823519 | ||
|
|
7fde6ac490 | ||
|
|
efeab1f498 | ||
|
|
22da836094 | ||
|
|
1ed0288d9c | ||
|
|
786a27b9e5 | ||
|
|
fb347dc618 | ||
|
|
f5fb4e5a97 | ||
|
|
4258402047 | ||
|
|
e6cb7a1c9b | ||
|
|
6107857b7b | ||
|
|
6041aaba97 | ||
|
|
cbf096b05e | ||
|
|
471eb5913c | ||
|
|
6a2774719f | ||
|
|
4ee75be3a3 | ||
|
|
72435b0aba | ||
|
|
dee5d1c42d | ||
|
|
7391382349 | ||
|
|
db211eefbf | ||
|
|
c81e389996 | ||
|
|
65803171a7 | ||
|
|
7efcb946c4 | ||
|
|
722652222a | ||
|
|
777b0ba965 | ||
|
|
1b5f632e0f | ||
|
|
3cf19c6e5e | ||
|
|
f1f4ec315a | ||
|
|
ae541d3fab | ||
|
|
392f645f4d | ||
|
|
129b727058 | ||
|
|
00f06c9beb | ||
|
|
c2e7a06392 | ||
|
|
f58ebdff7a | ||
|
|
80d18c18c9 | ||
|
|
d4259ea53b | ||
|
|
5d823f3a91 | ||
|
|
1f5fe3a3e3 | ||
|
|
4556d3c0c8 | ||
|
|
1c6742e6a0 | ||
|
|
dad821bb22 | ||
|
|
d128efcb6a | ||
|
|
4736fb4fc9 | ||
|
|
767a2e1790 | ||
|
|
03ae509f32 | ||
|
|
adfd9d7b1d | ||
|
|
841af2b426 | ||
|
|
8118056c03 | ||
|
|
f43ac8543e | ||
|
|
c8683bee80 | ||
|
|
5d292b5660 | ||
|
|
f541871f5d | ||
|
|
354183b10d | ||
|
|
7455813e57 | ||
|
|
21e44c45d9 | ||
|
|
4bb4037dbe | ||
|
|
f307b25804 | ||
|
|
d8cea87562 | ||
|
|
537a4bedb4 | ||
|
|
d460333efb | ||
|
|
25e43c6022 | ||
|
|
092fcac0b4 | ||
|
|
a795b19327 | ||
|
|
24bbf376ce | ||
|
|
e128b170a0 | ||
|
|
95a59d2f72 | ||
|
|
4b0c008489 | ||
|
|
2a84f9b8fc | ||
|
|
b7858abfc0 | ||
|
|
40fa6674a9 | ||
|
|
7e09a57b39 | ||
|
|
dcfa6fd507 | ||
|
|
40672b0b78 | ||
|
|
4aea743279 | ||
|
|
d82a164d0d | ||
|
|
fe7108305a | ||
|
|
17a64fe91c | ||
|
|
3e19ebd2ea | ||
|
|
a835d9cf85 | ||
|
|
fc8bc4b5c0 | ||
|
|
264d89d641 | ||
|
|
857ebd3daf | ||
|
|
b8bdea6b26 | ||
|
|
730658f72f | ||
|
|
035be44867 | ||
|
|
8da0896b79 | ||
|
|
cd28e60337 | ||
|
|
6cdac3c573 | ||
|
|
7503b5b33a | ||
|
|
ee72bf1cfd | ||
|
|
d338dd45d7 | ||
|
|
ec0b579563 | ||
|
|
10b96f6351 | ||
|
|
181093173d | ||
|
|
b58dea8d1b | ||
|
|
634afc4124 | ||
|
|
97b7a9cf77 | ||
|
|
33372cbd40 | ||
|
|
27f29e424b | ||
|
|
eda9c2858f | ||
|
|
5dead37a9d | ||
|
|
dcea2c0f8b | ||
|
|
75dae934a1 | ||
|
|
b06dfb9ccc | ||
|
|
381c97caa4 | ||
|
|
abd4674931 | ||
|
|
f72014d097 | ||
|
|
7324c4f85f | ||
|
|
6de77799de | ||
|
|
733ae918f0 | ||
|
|
1e2524e15a | ||
|
|
855d20f6f3 | ||
|
|
fc052ff728 | ||
|
|
b543c20a94 | ||
|
|
f533ab6da6 | ||
|
|
508303077b | ||
|
|
4d970f8bfc | ||
|
|
2097e1945b | ||
|
|
fef9abdcc0 | ||
|
|
e6cb6919c0 | ||
|
|
d454af90f1 | ||
|
|
8be5584494 | ||
|
|
e10e5059e8 | ||
|
|
039bfd4e30 | ||
|
|
0295aa38a2 | ||
|
|
f1f8a75496 | ||
|
|
2c09d43494 | ||
|
|
46471ea327 | ||
|
|
79dd0c04ed | ||
|
|
fb6cfffd39 | ||
|
|
4f8efba824 | ||
|
|
cd29ebefd0 | ||
|
|
a13fbad374 | ||
|
|
7099c66205 | ||
|
|
40e7aed59e | ||
|
|
58a00df238 | ||
|
|
3589571bfd | ||
|
|
877af247a8 | ||
|
|
79ae7f9211 | ||
|
|
26e9a3103d | ||
|
|
bb6a418523 | ||
|
|
50289750b3 | ||
|
|
dc26b30094 | ||
|
|
93b99dd140 | ||
|
|
a4759a1911 | ||
|
|
11c47cb133 | ||
|
|
1a1124462e | ||
|
|
294b276fc7 | ||
|
|
f730577277 | ||
|
|
7bb6e2dc0d | ||
|
|
883ab327cc | ||
|
|
0c96a2f03b | ||
|
|
dfef0df71a | ||
|
|
12eb888e54 | ||
|
|
bd4fdf295e | ||
|
|
faec7219b2 | ||
|
|
45671e4a27 | ||
|
|
daefde88fe | ||
|
|
07675840a5 | ||
|
|
238f3c7430 | ||
|
|
81c6027592 | ||
|
|
506d907e40 | ||
|
|
f29f6090f5 | ||
|
|
8164fad404 | ||
|
|
565045ef71 | ||
|
|
d0c43217b5 | ||
|
|
e2cf3fb1a3 | ||
|
|
436789ad96 | ||
|
|
3c28aa6f17 | ||
|
|
f6bfa3dcca | ||
|
|
527dd4cdb8 | ||
|
|
c075fd0bcb | ||
|
|
46a9d7fc0b | ||
|
|
2d6eec7b5c | ||
|
|
65698b7f2e | ||
|
|
f012c43be0 | ||
|
|
103f5feda5 | ||
|
|
3034f4a508 | ||
|
|
aef28b2f8f | ||
|
|
655a096343 | ||
|
|
71fc0dc968 | ||
|
|
74958567a4 | ||
|
|
5bcc2c2ab9 | ||
|
|
0880c2ab97 | ||
|
|
7be3dea82c | ||
|
|
9f93625c14 | ||
|
|
7624043a41 | ||
|
|
eba35e2e48 | ||
|
|
e77f47a85a | ||
|
|
48a2cd6d79 | ||
|
|
f77cec311e | ||
|
|
bf55e58e63 | ||
|
|
d1d7e1076b | ||
|
|
cdb916ed3f | ||
|
|
8f2193dc8d | ||
|
|
4d090d23f1 | ||
|
|
7b62885476 | ||
|
|
497836dbc8 | ||
|
|
3192d6b77d | ||
|
|
22a0a7b93a | ||
|
|
70d12ffa05 | ||
|
|
6ca918d0cf | ||
|
|
ce8c0d6c09 | ||
|
|
1ada4aef84 | ||
|
|
1ad368c8b7 | ||
|
|
356aaa7dc6 | ||
|
|
acd0bf6a26 | ||
|
|
32cb7c2f0b | ||
|
|
688fb3eb89 | ||
|
|
8433ad84ea | ||
|
|
a24faed569 | ||
|
|
585f925055 | ||
|
|
12be9f5727 | ||
|
|
c4e9ea8cc6 | ||
|
|
2deabac079 | ||
|
|
a5ddfa7d91 | ||
|
|
8da6676a02 | ||
|
|
ce17af4221 | ||
|
|
4b66297ea0 | ||
|
|
a62b7ed928 | ||
|
|
1dd24ca6d2 | ||
|
|
0dc95a882f | ||
|
|
1ad06aa53b | ||
|
|
c9d56cddf2 | ||
|
|
0d6d9e8586 | ||
|
|
f9aad7a49b | ||
|
|
09cd79495f | ||
|
|
a166c20818 | ||
|
|
d936b46d3a | ||
|
|
3b4a03b92b | ||
|
|
2256ef8961 | ||
|
|
ccdc70a5ce | ||
|
|
b30a5c2f90 | ||
|
|
2e5899c129 | ||
|
|
a5780598b3 | ||
|
|
ba783b3a0f | ||
|
|
441d88d1c7 | ||
|
|
0dcd576600 | ||
|
|
99b1291d17 | ||
|
|
734109d9c2 | ||
|
|
e732344ef1 | ||
|
|
92485032b2 | ||
|
|
d701a085c2 | ||
|
|
397da03865 | ||
|
|
fef0b1ffe4 | ||
|
|
0a2fcfac01 | ||
|
|
d498a4bb08 | ||
|
|
6a7aa46ef7 | ||
|
|
370c1ea007 | ||
|
|
7f5ebd211c | ||
|
|
60113dfe3b | ||
|
|
44529da852 | ||
|
|
ff6acc6ed5 | ||
|
|
e1dcc557d6 | ||
|
|
1b00d808d7 | ||
|
|
d303f30796 | ||
|
|
74d36d78ef | ||
|
|
b66c0973cc | ||
|
|
da68f73296 | ||
|
|
909c501b07 | ||
|
|
79f1ee2fc0 | ||
|
|
b428dceeaf | ||
|
|
22657a5e65 | ||
|
|
4a78c6d81b | ||
|
|
305b1f6d96 | ||
|
|
6c3491ad31 | ||
|
|
9f4fc27389 | ||
|
|
bc31041edb | ||
|
|
51bd147422 | ||
|
|
9d2cc3b0fa | ||
|
|
a88d22ea11 | ||
|
|
0ceb31184d | ||
|
|
fa61579382 | ||
|
|
21dfa4300d | ||
|
|
162c2bcf31 | ||
|
|
29436fa056 | ||
|
|
a0c1f4ac57 | ||
|
|
7013716aaa | ||
|
|
182192f83c | ||
|
|
34b7165fde | ||
|
|
7e6c8a7286 | ||
|
|
ac50c1e44b | ||
|
|
06e42090f7 | ||
|
|
f7d9c6c42d | ||
|
|
65a54d7714 | ||
|
|
9b5d105fc3 | ||
|
|
1d449bd9a6 | ||
|
|
08eb1e6c31 | ||
|
|
231d1477e5 | ||
|
|
0586a56f39 | ||
|
|
040eac77cc | ||
|
|
7c7b702064 | ||
|
|
44f5f53605 | ||
|
|
6513d9d923 | ||
|
|
9b5ca0b09d | ||
|
|
d05b61db4a | ||
|
|
ca21ce4071 | ||
|
|
01f255161c | ||
|
|
0f686e82a3 | ||
|
|
68e8ddcaff | ||
|
|
bc05ecebf6 | ||
|
|
ba0e35411c | ||
|
|
7fd22f7b2e | ||
|
|
ca1f082251 | ||
|
|
7014f81e17 | ||
|
|
2476889195 | ||
|
|
9cac3fa8f5 | ||
|
|
bdc08b5158 | ||
|
|
c6bfe55857 | ||
|
|
e896522616 | ||
|
|
5e500523a0 | ||
|
|
97dd79db6c | ||
|
|
536ea6858e | ||
|
|
974222c626 | ||
|
|
88d9eca848 | ||
|
|
0f552ed673 | ||
|
|
38a48e5741 | ||
|
|
88b0e03a4e | ||
|
|
d71a145ec1 | ||
|
|
ea351a7b52 | ||
|
|
d92f0175c7 | ||
|
|
6a622867ca | ||
|
|
f81341a48b | ||
|
|
cd6e908241 | ||
|
|
84b28c6a13 | ||
|
|
d331208560 | ||
|
|
a72ea0db60 | ||
|
|
7b67ed0b5a | ||
|
|
bd3be8e0b1 | ||
|
|
b8df719d5c | ||
|
|
dab81bd315 | ||
|
|
ad3f3fc561 | ||
|
|
a5d3dd4059 | ||
|
|
2b604a3016 | ||
|
|
40a391408c | ||
|
|
6d8e616902 | ||
|
|
6b6b4709b3 | ||
|
|
9a60cf54ec | ||
|
|
7a27a05940 | ||
|
|
e98ddf7987 | ||
|
|
3fa142cf39 | ||
|
|
112dc36dd5 | ||
|
|
ea42174d07 | ||
|
|
6342da0970 | ||
|
|
f2f2084e79 | ||
|
|
e210ccc57e | ||
|
|
2f4fa80414 | ||
|
|
56cc56b692 | ||
|
|
0aa9769fb0 | ||
|
|
48ec5d4355 | ||
|
|
282a382326 | ||
|
|
576d7047ab | ||
|
|
372042a082 | ||
|
|
439219be7e | ||
|
|
9cad5639ff | ||
|
|
57fe99d4be | ||
|
|
c792f3e4ab | ||
|
|
ae23e5f426 | ||
|
|
d8b0b66c6c | ||
|
|
98eb40e563 | ||
|
|
bb70df8e5b | ||
|
|
0704516245 | ||
|
|
09475bf8ac | ||
|
|
7859184179 | ||
|
|
a7862df616 | ||
|
|
865debda22 | ||
|
|
a738a66b5c | ||
|
|
4b2e5ee8b9 | ||
|
|
d0f187ad3b | ||
|
|
ebbb973271 | ||
|
|
403f2d1664 | ||
|
|
92a511d385 | ||
|
|
e08c581cf1 | ||
|
|
39fdc690a0 | ||
|
|
c8dae890c8 | ||
|
|
2fe5108263 | ||
|
|
1fff4d2ee3 | ||
|
|
a5b42a1a10 | ||
|
|
368baa42ef | ||
|
|
f83fdf5336 | ||
|
|
d78fe241ac | ||
|
|
5459ec9dae | ||
|
|
bac8e2e5a6 | ||
|
|
dc8b4e6e13 | ||
|
|
703fbc0ff5 | ||
|
|
ae0d8e8591 | ||
|
|
ba8b9ecbcb | ||
|
|
837119a948 | ||
|
|
9f6eb09585 | ||
|
|
4278f84603 | ||
|
|
9226e1f0d8 | ||
|
|
d5031ea824 | ||
|
|
4afd709d1f | ||
|
|
1d01e4c770 | ||
|
|
dd649c9485 | ||
|
|
1decf958d1 | ||
|
|
45836d7fd3 | ||
|
|
7d2bac5a38 | ||
|
|
055478cca8 | ||
|
|
7e3671911f | ||
|
|
dfdd46f6c1 | ||
|
|
aa1d71fb7a | ||
|
|
5e0b090992 | ||
|
|
1b1e76acfe | ||
|
|
a70909b130 | ||
|
|
5c47a30227 | ||
|
|
6945371778 | ||
|
|
b55419f9b9 | ||
|
|
8b2c2a596d | ||
|
|
9a3c738009 | ||
|
|
9a7ae3c97f | ||
|
|
cb1a1f4ada | ||
|
|
7160617107 | ||
|
|
a787d2f5b3 | ||
|
|
04cb7d4d6b | ||
|
|
73510dfd15 | ||
|
|
3b5eecf102 | ||
|
|
e4c36a6f30 | ||
|
|
c462753cc8 | ||
|
|
836d7a7e69 | ||
|
|
9c59abe42a | ||
|
|
da50d8ce87 | ||
|
|
bc774a3519 | ||
|
|
19a51abf33 | ||
|
|
d01b425e5d | ||
|
|
a8b578d538 | ||
|
|
43f0ce92d8 | ||
|
|
97f93fa0f2 | ||
|
|
198f02e797 | ||
|
|
152c0ef954 | ||
|
|
6924f83cb2 | ||
|
|
de81a4eada | ||
|
|
9cfa805947 | ||
|
|
ee961c2310 | ||
|
|
bd6e28d6a3 | ||
|
|
4786099a3a | ||
|
|
05681e387a | ||
|
|
4fe50bc7d0 | ||
|
|
4fbbc817c1 | ||
|
|
4094b7c03f | ||
|
|
66e07f986d | ||
|
|
beb7c16e94 | ||
|
|
1df9dd3515 | ||
|
|
3d84bd57f4 | ||
|
|
c97d255752 | ||
|
|
ce5b128f10 | ||
|
|
821dd53a1d | ||
|
|
e144b21e4b | ||
|
|
77bbb35a92 | ||
|
|
1210af9a4e | ||
|
|
a42861540e | ||
|
|
820f070c6b | ||
|
|
63d1b7a7a7 | ||
|
|
b416b96a39 | ||
|
|
e6180fe1b4 | ||
|
|
4e5e43f22e | ||
|
|
723ccc6c27 | ||
|
|
41187bdfb0 | ||
|
|
529cfe2abc | ||
|
|
e7cf84a8dd | ||
|
|
d1be488cd8 | ||
|
|
ac5185f73e | ||
|
|
b7c7df24ba | ||
|
|
4b76c684bb | ||
|
|
242af439b8 | ||
|
|
e06658cb24 | ||
|
|
22bc75ee3e | ||
|
|
93d6151800 | ||
|
|
671f120e26 | ||
|
|
785b84e603 | ||
|
|
23dc7a7fba | ||
|
|
2abb02ecd7 | ||
|
|
719583f14a | ||
|
|
adce29c885 | ||
|
|
49e6fbce78 | ||
|
|
57b81c078e | ||
|
|
9e031c1d11 | ||
|
|
0043e68d4c | ||
|
|
937ca1db9f | ||
|
|
7599c82d48 | ||
|
|
d2dddfb82a | ||
|
|
d6b7995c16 | ||
|
|
e2c7584361 | ||
|
|
9c5c2a2c86 | ||
|
|
28ed9b9a51 | ||
|
|
eb70fef8ca | ||
|
|
a219cfcda3 | ||
|
|
ad3f7dbfa0 | ||
|
|
5934846612 | ||
|
|
22746d3c9f | ||
|
|
8c8466fa6e | ||
|
|
7fc6b1b259 | ||
|
|
39343555d6 | ||
|
|
87e2400cbb | ||
|
|
07ce2434bf | ||
|
|
56126d0d2d | ||
|
|
56c8ecffc1 | ||
|
|
ea6b3e973b | ||
|
|
0cfa36a58a | ||
|
|
8a94ac25d5 | ||
|
|
82b8aaaeb6 | ||
|
|
9344d25488 | ||
|
|
c9cdc1b27f | ||
|
|
5742473cc8 | ||
|
|
1a0503367b | ||
|
|
0b462407ae | ||
|
|
7618b0b8e4 | ||
|
|
85f0cb2d0c | ||
|
|
9f6a9f4c1f | ||
|
|
10fcff6c38 | ||
|
|
a3fa597921 | ||
|
|
bc0998a905 | ||
|
|
1d148f353a | ||
|
|
4e36ce34c1 | ||
|
|
42807fcd9e | ||
|
|
57dbab5d6b | ||
|
|
badf8d95f1 | ||
|
|
9a86c65abc | ||
|
|
f32f23d6cc | ||
|
|
a2a693e722 | ||
|
|
e0a0c8d4bf | ||
|
|
d26636d566 | ||
|
|
34e75630cc | ||
|
|
7720b6e385 | ||
|
|
40a3435b8d | ||
|
|
1a71533511 | ||
|
|
e13cf65a5f | ||
|
|
c92ac30601 | ||
|
|
4c63b0e76a | ||
|
|
2d6d099fed | ||
|
|
07c44847c2 | ||
|
|
13e88ae970 | ||
|
|
259b15dec5 | ||
|
|
5679751208 | ||
|
|
b62d126df8 | ||
|
|
7fc4f2a32c | ||
|
|
ae124b8684 | ||
|
|
702d76dbd0 | ||
|
|
498f47e1ec | ||
|
|
1aabab5fdc | ||
|
|
21cfe59ff7 | ||
|
|
33e1dad680 | ||
|
|
b92c9a1a81 | ||
|
|
f95a88fcd9 | ||
|
|
f4d24f05e1 | ||
|
|
7f7935cf78 | ||
|
|
14e4176758 | ||
|
|
020b5dc7ac | ||
|
|
7ee3897740 | ||
|
|
d3d7a8ef09 | ||
|
|
c2a3a7cd3f | ||
|
|
93084cdc89 | ||
|
|
9aab3f2be0 | ||
|
|
b0b3e49a54 | ||
|
|
f7489dc8f1 | ||
|
|
978a7a21ae | ||
|
|
6fe11633b0 | ||
|
|
064d6e570e | ||
|
|
0101d5ebef | ||
|
|
59d9891948 | ||
|
|
ce03e4c7b8 | ||
|
|
788a657cee | ||
|
|
a4d017f5e5 | ||
|
|
fb56766ca9 | ||
|
|
f6c30cbafa | ||
|
|
b804d0f019 | ||
|
|
06f0850427 | ||
|
|
14d3686c9f | ||
|
|
ac24ffb21e | ||
|
|
775fe302a7 | ||
|
|
cbdb1807ce | ||
|
|
18450d0abe | ||
|
|
dca7060d5e | ||
|
|
9015deb126 | ||
|
|
23b15e764b | ||
|
|
2db2d2ef5e | ||
|
|
1f55a15467 | ||
|
|
8e9a613122 | ||
|
|
e13400c919 | ||
|
|
b33eb26dee | ||
|
|
374dc30d3d | ||
|
|
e3db480f6f | ||
|
|
350809dc5d | ||
|
|
d3145014a0 | ||
|
|
a7b0a124c3 | ||
|
|
522d1e7b4b | ||
|
|
8c9571376e | ||
|
|
7a815c1b7c | ||
|
|
bbe8e6a909 | ||
|
|
b6f1f2669a | ||
|
|
225d89998b | ||
|
|
f459454afe | ||
|
|
0ecd5addb1 | ||
|
|
fc13a7f1fa | ||
|
|
646af596d8 | ||
|
|
6910fcb208 | ||
|
|
471191075d | ||
|
|
daa0e436ba | ||
|
|
4e920d2c9d | ||
|
|
7241bf5b74 | ||
|
|
441335d924 | ||
|
|
36de5189c7 | ||
|
|
99f4eefb61 | ||
|
|
711a61f3b4 | ||
|
|
98b56ab23d | ||
|
|
45bb4483ba | ||
|
|
3f9f2833f6 | ||
|
|
37b8913925 | ||
|
|
b23336af96 | ||
|
|
c4063ce439 | ||
|
|
43f3b1ad0c | ||
|
|
13c237c7ef | ||
|
|
17aa470760 | ||
|
|
4b82872ebf | ||
|
|
57553c3b1a | ||
|
|
1f56115704 | ||
|
|
794e29c0ab | ||
|
|
e2ab215324 | ||
|
|
5e795297b3 | ||
|
|
a4c6b9ae01 | ||
|
|
cd2931ad9b | ||
|
|
7ee0b6cc37 | ||
|
|
d66f976628 | ||
|
|
8dabf81df1 | ||
|
|
50b473c883 | ||
|
|
be0749f591 | ||
|
|
ceba1dde27 | ||
|
|
936bc2aa15 | ||
|
|
0f8313659e | ||
|
|
0ec656eb71 | ||
|
|
ac34afb403 | ||
|
|
4edbb7c64c | ||
|
|
2b112ea593 | ||
|
|
01e2a38e5a | ||
|
|
837de32496 | ||
|
|
741fca1bb7 | ||
|
|
730e57b08f | ||
|
|
08260a6b94 | ||
|
|
325e3305fc | ||
|
|
997d86acc6 | ||
|
|
d1a861fa80 | ||
|
|
0ac524308d | ||
|
|
9ed3bdc848 | ||
|
|
09adf18f1a | ||
|
|
856319dc8a | ||
|
|
f80c97e477 | ||
|
|
676cdd6679 | ||
|
|
115ce93562 | ||
|
|
88771fa013 | ||
|
|
9f43defb6e | ||
|
|
4c548a61c8 | ||
|
|
36eb0b3d77 | ||
|
|
1f71e1ca43 | ||
|
|
0771ff2cea | ||
|
|
e8ec9528ab | ||
|
|
126252a7e6 | ||
|
|
c14e6c0819 | ||
|
|
33f1aa5bad | ||
|
|
5e23f96dd4 | ||
|
|
b664bd7935 | ||
|
|
eb261e53a6 | ||
|
|
41b7372ead | ||
|
|
013ab26c86 | ||
|
|
3aba072cef | ||
|
|
4bc48718b2 | ||
|
|
6deed86deb | ||
|
|
0eb0641cac | ||
|
|
55e5cc1341 | ||
|
|
5112d9e5fd | ||
|
|
4610ffa942 | ||
|
|
a7f68aed3e | ||
|
|
017dca198c | ||
|
|
5b7fc9056c | ||
|
|
0552aad91b | ||
|
|
4f55c0ec22 | ||
|
|
2f07e92adb | ||
|
|
07b89c7673 | ||
|
|
e904f813b4 | ||
|
|
fb924dd7bf | ||
|
|
3d29d11ac2 | ||
|
|
ce5c073fe5 | ||
|
|
cdf15b458a | ||
|
|
ff622eeab5 | ||
|
|
1302af4c43 | ||
|
|
79522b2fc2 | ||
|
|
94a88c26f4 | ||
|
|
77a71ef2b7 | ||
|
|
d1e11d48d4 | ||
|
|
50820010fe | ||
|
|
fec1ef7c91 | ||
|
|
0ff7ff3ec7 | ||
|
|
c834912aa9 | ||
|
|
9bd8deb260 | ||
|
|
d22e150320 | ||
|
|
a7f749c9af | ||
|
|
0379235f47 | ||
|
|
59e371f463 | ||
|
|
bb071db92e | ||
|
|
126e9a84a5 | ||
|
|
cb0707c0fc | ||
|
|
67270040a5 | ||
|
|
83e516154c | ||
|
|
c154f5881b | ||
|
|
226327cf66 | ||
|
|
fe0e399385 | ||
|
|
5ae6e7cbfa | ||
|
|
06a8c40b36 | ||
|
|
c7a87e0e0b | ||
|
|
e870829e64 | ||
|
|
feff510b9f | ||
|
|
39d6420c0c | ||
|
|
982c10de35 | ||
|
|
e74a1a37c8 | ||
|
|
09edea3f5c | ||
|
|
b293a3eb86 | ||
|
|
312352faa8 | ||
|
|
e8a63b87c3 |
503 changed files with 83484 additions and 27027 deletions
|
|
@ -5,33 +5,42 @@ environment:
|
|||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
MSVC: amd64
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-fcommon"
|
||||
- MSYSTEM: MINGW32
|
||||
CPU: i686
|
||||
MSVC: x86
|
||||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
- MSYSTEM: MINGW32
|
||||
CPU: i686
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-fcommon"
|
||||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
MSVC: amd64
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
CONFIG_FLAGS:
|
||||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
CONFIG_FLAGS:
|
||||
EXTRA_CFLAGS: "-fcommon"
|
||||
- MSYSTEM: MINGW32
|
||||
CPU: i686
|
||||
MSVC: x86
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
- MSYSTEM: MINGW64
|
||||
CPU: x86_64
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
CONFIG_FLAGS:
|
||||
- MSYSTEM: MINGW32
|
||||
CPU: i686
|
||||
CONFIG_FLAGS: --enable-debug
|
||||
CONFIG_FLAGS:
|
||||
EXTRA_CFLAGS: "-fcommon"
|
||||
|
||||
install:
|
||||
- set PATH=c:\msys64\%MSYSTEM%\bin;c:\msys64\usr\bin;%PATH%
|
||||
- if defined MSVC call "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %MSVC%
|
||||
- if defined MSVC pacman --noconfirm -Rsc mingw-w64-%CPU%-gcc gcc
|
||||
- pacman --noconfirm -Suy mingw-w64-%CPU%-make
|
||||
- pacman --noconfirm -Syuu
|
||||
- pacman --noconfirm -S autoconf
|
||||
|
||||
build_script:
|
||||
- bash -c "autoconf"
|
||||
|
|
|
|||
122
.clang-format
Normal file
122
.clang-format
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# jemalloc targets clang-format version 8. We include every option it supports
|
||||
# here, but comment out the ones that aren't relevant for us.
|
||||
---
|
||||
# AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: true
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: AllDefinitions
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
# AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterControlStatement: true
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
# BreakAfterJavaFieldAnnotations: true
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: true
|
||||
# BreakConstructorInitializers: BeforeColon
|
||||
# BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 80
|
||||
# CommentPragmas: ''
|
||||
# CompactNamespaces: true
|
||||
# ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
# ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros: [ ql_foreach, qr_foreach, ]
|
||||
# IncludeBlocks: Preserve
|
||||
# IncludeCategories:
|
||||
# - Regex: '^<.*\.h(pp)?>'
|
||||
# Priority: 1
|
||||
# IncludeIsMainRegex: ''
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 8
|
||||
IndentWrappedFunctionNames: false
|
||||
# JavaImportGroups: []
|
||||
# JavaScriptQuotes: Leave
|
||||
# JavaScriptWrapImports: True
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
Language: Cpp
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
# NamespaceIndentation: None
|
||||
# ObjCBinPackProtocolList: Auto
|
||||
# ObjCBlockIndentWidth: 2
|
||||
# ObjCSpaceAfterProperty: false
|
||||
# ObjCSpaceBeforeProtocolList: false
|
||||
|
||||
PenaltyBreakAssignment: 100
|
||||
PenaltyBreakBeforeFirstCallParameter: 100
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
# PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
# RawStringFormats:
|
||||
# - Language: TextProto
|
||||
# Delimiters:
|
||||
# - 'pb'
|
||||
# - 'proto'
|
||||
# EnclosingFunctions:
|
||||
# - 'PARSE_TEXT_PROTO'
|
||||
# BasedOnStyle: google
|
||||
# - Language: Cpp
|
||||
# Delimiters:
|
||||
# - 'cc'
|
||||
# - 'cpp'
|
||||
# BasedOnStyle: llvm
|
||||
# CanonicalDelimiter: 'cc'
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SpaceAfterCStyleCast: false
|
||||
# SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
# SpaceBeforeCpp11BracedList: false
|
||||
# SpaceBeforeCtorInitializerColon: true
|
||||
# SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
# SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
# SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
# Standard: Cpp11
|
||||
# This is nominally supported in clang-format version 8, but not in the build
|
||||
# used by some of the core jemalloc developers.
|
||||
# StatementMacros: []
|
||||
TabWidth: 8
|
||||
UseTab: ForIndentation
|
||||
...
|
||||
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
554185356bf990155df8d72060c4efe993642baf
|
||||
34f359e0ca613b5f9d970e9b2152a5203c9df8d6
|
||||
10
.github/workflows/check_formatting.yaml
vendored
Normal file
10
.github/workflows/check_formatting.yaml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
name: 'Check Formatting'
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
check-formatting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Check for trailing whitespace
|
||||
run: scripts/check_trailing_whitespace.sh
|
||||
66
.github/workflows/freebsd-ci.yml
vendored
Normal file
66
.github/workflows/freebsd-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# This config file is generated by ./scripts/gen_gh_actions.py.
|
||||
# Do not edit by hand.
|
||||
|
||||
name: FreeBSD CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, ci_travis ]
|
||||
pull_request:
|
||||
branches: [ dev ]
|
||||
|
||||
jobs:
|
||||
test-freebsd:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
debug: ['--enable-debug', '--disable-debug']
|
||||
prof: ['--enable-prof', '--disable-prof']
|
||||
arch: ['64-bit', '32-bit']
|
||||
uncommon:
|
||||
- ''
|
||||
- '--with-lg-page=16 --with-malloc-conf=tcache:false'
|
||||
|
||||
name: FreeBSD (${{ matrix.arch }}, debug=${{ matrix.debug }}, prof=${{ matrix.prof }}${{ matrix.uncommon && ', uncommon' || '' }})
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Test on FreeBSD
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
release: '15.0'
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y autoconf gmake
|
||||
run: |
|
||||
# Verify we're running in FreeBSD
|
||||
echo "==== System Information ===="
|
||||
uname -a
|
||||
freebsd-version
|
||||
echo "============================"
|
||||
|
||||
# Set compiler flags for 32-bit if needed
|
||||
if [ "${{ matrix.arch }}" = "32-bit" ]; then
|
||||
export CC="cc -m32"
|
||||
export CXX="c++ -m32"
|
||||
fi
|
||||
|
||||
# Generate configure script
|
||||
autoconf
|
||||
|
||||
# Configure with matrix options
|
||||
./configure --with-jemalloc-prefix=ci_ ${{ matrix.debug }} ${{ matrix.prof }} ${{ matrix.uncommon }}
|
||||
|
||||
# Get CPU count for parallel builds
|
||||
export JFLAG=$(sysctl -n kern.smp.cpus)
|
||||
|
||||
gmake -j${JFLAG}
|
||||
gmake -j${JFLAG} tests
|
||||
gmake check
|
||||
|
||||
|
||||
|
||||
695
.github/workflows/linux-ci.yml
vendored
Normal file
695
.github/workflows/linux-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,695 @@
|
|||
# This config file is generated by ./scripts/gen_gh_actions.py.
|
||||
# Do not edit by hand.
|
||||
|
||||
name: Linux CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, ci_travis ]
|
||||
pull_request:
|
||||
branches: [ dev ]
|
||||
|
||||
jobs:
|
||||
test-linux:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-prof
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-stats
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-libdl
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-opt-safety-checks
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --with-lg-page=16
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: --enable-prof
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: --disable-stats
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: --disable-libdl
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: --enable-opt-safety-checks
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: --with-lg-page=16
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: --enable-prof
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: --disable-stats
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: --disable-libdl
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: --enable-opt-safety-checks
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: --with-lg-page=16
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
COMPILER_FLAGS: -m32
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --enable-prof"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --disable-stats"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --disable-libdl"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --enable-opt-safety-checks"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --with-lg-page=16"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --disable-stats"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --disable-libdl"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-opt-safety-checks"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --with-lg-page=16"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --disable-libdl"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --enable-opt-safety-checks"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --with-lg-page=16"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-stats --with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-libdl --enable-opt-safety-checks"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-libdl --with-lg-page=16"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-libdl --enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-libdl --with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-libdl --with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-libdl --with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--disable-libdl --with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-opt-safety-checks --with-lg-page=16"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-opt-safety-checks --enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-opt-safety-checks --with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-opt-safety-checks --with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-opt-safety-checks --with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-opt-safety-checks --with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-lg-page=16 --enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-lg-page=16 --with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-lg-page=16 --with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-lg-page=16 --with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-lg-page=16 --with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr --with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr --with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr --with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr --with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false,dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false,percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false,background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=dss:primary,percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=dss:primary,background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=percpu_arena:percpu,background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --disable-cache-oblivious --enable-stats --enable-log --enable-prof"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-debug --enable-experimental-smallocx --enable-stats --enable-prof"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show OS version
|
||||
run: |
|
||||
echo "=== System Information ==="
|
||||
uname -a
|
||||
echo ""
|
||||
echo "=== Architecture ==="
|
||||
uname -m
|
||||
arch
|
||||
echo ""
|
||||
echo "=== OS Release ==="
|
||||
cat /etc/os-release || true
|
||||
echo ""
|
||||
echo "=== CPU Info ==="
|
||||
lscpu | grep -E "Architecture|CPU op-mode|Byte Order|CPU\(s\):" || true
|
||||
|
||||
- name: Install dependencies (32-bit)
|
||||
if: matrix.env.CROSS_COMPILE_32BIT == 'yes'
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-multilib g++-multilib libc6-dev-i386
|
||||
|
||||
- name: Build and test
|
||||
env:
|
||||
CC: ${{ matrix.env.CC }}
|
||||
CXX: ${{ matrix.env.CXX }}
|
||||
COMPILER_FLAGS: ${{ matrix.env.COMPILER_FLAGS }}
|
||||
CONFIGURE_FLAGS: ${{ matrix.env.CONFIGURE_FLAGS }}
|
||||
EXTRA_CFLAGS: ${{ matrix.env.EXTRA_CFLAGS }}
|
||||
run: |
|
||||
# Verify the script generates the same output
|
||||
./scripts/gen_gh_actions.py > gh_actions_script.yml
|
||||
|
||||
# Run autoconf
|
||||
autoconf
|
||||
|
||||
# Configure with flags
|
||||
if [ -n "$COMPILER_FLAGS" ]; then
|
||||
./configure CC="${CC} ${COMPILER_FLAGS}" CXX="${CXX} ${COMPILER_FLAGS}" $CONFIGURE_FLAGS
|
||||
else
|
||||
./configure $CONFIGURE_FLAGS
|
||||
fi
|
||||
|
||||
# Build
|
||||
make -j3
|
||||
make -j3 tests
|
||||
|
||||
# Run tests
|
||||
make check
|
||||
|
||||
|
||||
test-linux-arm64:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-prof
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-stats
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-libdl
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-opt-safety-checks
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --with-lg-page=16
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-lg-page=16 --with-lg-hugepage=29"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--enable-prof --enable-prof-frameptr"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=dss:primary"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=background_thread:true"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show OS version
|
||||
run: |
|
||||
echo "=== System Information ==="
|
||||
uname -a
|
||||
echo ""
|
||||
echo "=== Architecture ==="
|
||||
uname -m
|
||||
arch
|
||||
echo ""
|
||||
echo "=== OS Release ==="
|
||||
cat /etc/os-release || true
|
||||
echo ""
|
||||
echo "=== CPU Info ==="
|
||||
lscpu | grep -E "Architecture|CPU op-mode|Byte Order|CPU\(s\):" || true
|
||||
|
||||
- name: Install dependencies (32-bit)
|
||||
if: matrix.env.CROSS_COMPILE_32BIT == 'yes'
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-multilib g++-multilib libc6-dev-i386
|
||||
|
||||
- name: Build and test
|
||||
env:
|
||||
CC: ${{ matrix.env.CC }}
|
||||
CXX: ${{ matrix.env.CXX }}
|
||||
COMPILER_FLAGS: ${{ matrix.env.COMPILER_FLAGS }}
|
||||
CONFIGURE_FLAGS: ${{ matrix.env.CONFIGURE_FLAGS }}
|
||||
EXTRA_CFLAGS: ${{ matrix.env.EXTRA_CFLAGS }}
|
||||
run: |
|
||||
# Verify the script generates the same output
|
||||
./scripts/gen_gh_actions.py > gh_actions_script.yml
|
||||
|
||||
# Run autoconf
|
||||
autoconf
|
||||
|
||||
# Configure with flags
|
||||
if [ -n "$COMPILER_FLAGS" ]; then
|
||||
./configure CC="${CC} ${COMPILER_FLAGS}" CXX="${CXX} ${COMPILER_FLAGS}" $CONFIGURE_FLAGS
|
||||
else
|
||||
./configure $CONFIGURE_FLAGS
|
||||
fi
|
||||
|
||||
# Build
|
||||
make -j3
|
||||
make -j3 tests
|
||||
|
||||
# Run tests
|
||||
make check
|
||||
|
||||
|
||||
|
||||
212
.github/workflows/macos-ci.yml
vendored
Normal file
212
.github/workflows/macos-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# This config file is generated by ./scripts/gen_gh_actions.py.
|
||||
# Do not edit by hand.
|
||||
|
||||
name: macOS CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, ci_travis ]
|
||||
pull_request:
|
||||
branches: [ dev ]
|
||||
|
||||
jobs:
|
||||
test-macos:
|
||||
runs-on: macos-15-intel
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-stats
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-libdl
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-opt-safety-checks
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --with-lg-page=16
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show OS version
|
||||
run: |
|
||||
echo "=== macOS Version ==="
|
||||
sw_vers
|
||||
echo ""
|
||||
echo "=== Architecture ==="
|
||||
uname -m
|
||||
arch
|
||||
echo ""
|
||||
echo "=== CPU Info ==="
|
||||
sysctl -n machdep.cpu.brand_string
|
||||
sysctl -n hw.machine
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install autoconf
|
||||
|
||||
- name: Build and test
|
||||
env:
|
||||
CC: ${{ matrix.env.CC || 'gcc' }}
|
||||
CXX: ${{ matrix.env.CXX || 'g++' }}
|
||||
COMPILER_FLAGS: ${{ matrix.env.COMPILER_FLAGS }}
|
||||
CONFIGURE_FLAGS: ${{ matrix.env.CONFIGURE_FLAGS }}
|
||||
EXTRA_CFLAGS: ${{ matrix.env.EXTRA_CFLAGS }}
|
||||
run: |
|
||||
# Run autoconf
|
||||
autoconf
|
||||
|
||||
# Configure with flags
|
||||
if [ -n "$COMPILER_FLAGS" ]; then
|
||||
./configure CC="${CC} ${COMPILER_FLAGS}" CXX="${CXX} ${COMPILER_FLAGS}" $CONFIGURE_FLAGS
|
||||
else
|
||||
./configure $CONFIGURE_FLAGS
|
||||
fi
|
||||
|
||||
# Build
|
||||
make -j3
|
||||
make -j3 tests
|
||||
|
||||
# Run tests
|
||||
make check
|
||||
|
||||
|
||||
test-macos-arm64:
|
||||
runs-on: macos-15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-stats
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --disable-libdl
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-opt-safety-checks
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --with-lg-page=16
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-lg-page=16 --with-lg-hugepage=29"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=tcache:false"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: "--with-malloc-conf=percpu_arena:percpu"
|
||||
EXTRA_CFLAGS: "-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show OS version
|
||||
run: |
|
||||
echo "=== macOS Version ==="
|
||||
sw_vers
|
||||
echo ""
|
||||
echo "=== Architecture ==="
|
||||
uname -m
|
||||
arch
|
||||
echo ""
|
||||
echo "=== CPU Info ==="
|
||||
sysctl -n machdep.cpu.brand_string
|
||||
sysctl -n hw.machine
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install autoconf
|
||||
|
||||
- name: Build and test
|
||||
env:
|
||||
CC: ${{ matrix.env.CC || 'gcc' }}
|
||||
CXX: ${{ matrix.env.CXX || 'g++' }}
|
||||
COMPILER_FLAGS: ${{ matrix.env.COMPILER_FLAGS }}
|
||||
CONFIGURE_FLAGS: ${{ matrix.env.CONFIGURE_FLAGS }}
|
||||
EXTRA_CFLAGS: ${{ matrix.env.EXTRA_CFLAGS }}
|
||||
run: |
|
||||
# Run autoconf
|
||||
autoconf
|
||||
|
||||
# Configure with flags
|
||||
if [ -n "$COMPILER_FLAGS" ]; then
|
||||
./configure CC="${CC} ${COMPILER_FLAGS}" CXX="${CXX} ${COMPILER_FLAGS}" $CONFIGURE_FLAGS
|
||||
else
|
||||
./configure $CONFIGURE_FLAGS
|
||||
fi
|
||||
|
||||
# Build
|
||||
make -j3
|
||||
make -j3 tests
|
||||
|
||||
# Run tests
|
||||
make check
|
||||
|
||||
|
||||
|
||||
68
.github/workflows/static_analysis.yaml
vendored
Normal file
68
.github/workflows/static_analysis.yaml
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
name: 'Static Analysis'
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
static-analysis:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# We build libunwind ourselves because sadly the version
|
||||
# provided by Ubuntu via apt-get is much too old.
|
||||
- name: Check out libunwind
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: libunwind/libunwind
|
||||
path: libunwind
|
||||
ref: 'v1.6.2'
|
||||
github-server-url: 'https://github.com'
|
||||
- name: Install libunwind
|
||||
run: |
|
||||
cd libunwind
|
||||
autoreconf -i
|
||||
./configure --prefix=/usr
|
||||
make -s -j $(nproc) V=0
|
||||
sudo make -s install V=0
|
||||
cd ..
|
||||
rm -rf libunwind
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
# We download LLVM directly from the latest stable release
|
||||
# on GitHub, because this tends to be much newer than the
|
||||
# version available via apt-get in Ubuntu.
|
||||
- name: Download LLVM
|
||||
uses: dsaltares/fetch-gh-release-asset@master
|
||||
with:
|
||||
repo: 'llvm/llvm-project'
|
||||
version: 'tags/llvmorg-16.0.4'
|
||||
file: 'clang[+]llvm-.*x86_64-linux-gnu.*'
|
||||
regex: true
|
||||
target: 'llvm_assets/'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Install prerequisites
|
||||
id: install_prerequisites
|
||||
run: |
|
||||
tar -C llvm_assets -xaf llvm_assets/*.tar* &
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y jq bear python3-pip
|
||||
pip install codechecker
|
||||
echo "Extracting LLVM from tar" 1>&2
|
||||
wait
|
||||
echo "LLVM_BIN_DIR=$(echo llvm_assets/clang*/bin)" >> "$GITHUB_OUTPUT"
|
||||
- name: Run static analysis
|
||||
id: run_static_analysis
|
||||
run: >
|
||||
PATH="${{ steps.install_prerequisites.outputs.LLVM_BIN_DIR }}:$PATH"
|
||||
LDFLAGS='-L/usr/lib'
|
||||
scripts/run_static_analysis.sh static_analysis_results "$GITHUB_OUTPUT"
|
||||
- name: Upload static analysis results
|
||||
if: ${{ steps.run_static_analysis.outputs.HAS_STATIC_ANALYSIS_RESULTS }} == '1'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static_analysis_results
|
||||
path: static_analysis_results
|
||||
- name: Check static analysis results
|
||||
run: |
|
||||
if [[ "${{ steps.run_static_analysis.outputs.HAS_STATIC_ANALYSIS_RESULTS }}" == '1' ]]
|
||||
then
|
||||
echo "::error::Static analysis found issues with your code. Download the 'static_analysis_results' artifact from this workflow and view the 'index.html' file contained within it in a web browser locally for detailed results."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
155
.github/workflows/windows-ci.yml
vendored
Normal file
155
.github/workflows/windows-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
# This config file is generated by ./scripts/gen_gh_actions.py.
|
||||
# Do not edit by hand.
|
||||
|
||||
name: Windows CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, ci_travis ]
|
||||
pull_request:
|
||||
branches: [ dev ]
|
||||
|
||||
jobs:
|
||||
test-windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
EXTRA_CFLAGS: -fcommon
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: -fcommon
|
||||
- env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
EXTRA_CFLAGS: -fcommon
|
||||
- env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
- env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
EXTRA_CFLAGS: -fcommon
|
||||
- env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
- env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
CROSS_COMPILE_32BIT: yes
|
||||
CONFIGURE_FLAGS: --enable-debug
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show OS version
|
||||
shell: cmd
|
||||
run: |
|
||||
echo === Windows Version ===
|
||||
systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
|
||||
ver
|
||||
echo.
|
||||
echo === Architecture ===
|
||||
echo PROCESSOR_ARCHITECTURE=%PROCESSOR_ARCHITECTURE%
|
||||
echo.
|
||||
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.env.CROSS_COMPILE_32BIT == 'yes' && 'MINGW32' || 'MINGW64' }}
|
||||
update: true
|
||||
install: >-
|
||||
autotools
|
||||
git
|
||||
pacboy: >-
|
||||
make:p
|
||||
gcc:p
|
||||
binutils:p
|
||||
|
||||
- name: Build and test (MinGW-GCC)
|
||||
if: matrix.env.CC != 'cl.exe'
|
||||
shell: msys2 {0}
|
||||
env:
|
||||
CC: ${{ matrix.env.CC || 'gcc' }}
|
||||
CXX: ${{ matrix.env.CXX || 'g++' }}
|
||||
COMPILER_FLAGS: ${{ matrix.env.COMPILER_FLAGS }}
|
||||
CONFIGURE_FLAGS: ${{ matrix.env.CONFIGURE_FLAGS }}
|
||||
EXTRA_CFLAGS: ${{ matrix.env.EXTRA_CFLAGS }}
|
||||
run: |
|
||||
# Run autoconf
|
||||
autoconf
|
||||
|
||||
# Configure with flags
|
||||
if [ -n "$COMPILER_FLAGS" ]; then
|
||||
./configure CC="${CC} ${COMPILER_FLAGS}" CXX="${CXX} ${COMPILER_FLAGS}" $CONFIGURE_FLAGS
|
||||
else
|
||||
./configure $CONFIGURE_FLAGS
|
||||
fi
|
||||
|
||||
# Build (mingw32-make is the "make" command in MSYS2)
|
||||
mingw32-make -j3
|
||||
mingw32-make tests
|
||||
|
||||
# Run tests
|
||||
mingw32-make -k check
|
||||
|
||||
- name: Setup MSVC environment
|
||||
if: matrix.env.CC == 'cl.exe'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.env.CROSS_COMPILE_32BIT == 'yes' && 'x86' || 'x64' }}
|
||||
|
||||
- name: Build and test (MSVC)
|
||||
if: matrix.env.CC == 'cl.exe'
|
||||
shell: msys2 {0}
|
||||
env:
|
||||
CONFIGURE_FLAGS: ${{ matrix.env.CONFIGURE_FLAGS }}
|
||||
MSYS2_PATH_TYPE: inherit
|
||||
run: |
|
||||
# Export MSVC environment variables for configure
|
||||
export CC=cl.exe
|
||||
export CXX=cl.exe
|
||||
export AR=lib.exe
|
||||
export NM=dumpbin.exe
|
||||
export RANLIB=:
|
||||
|
||||
# Verify cl.exe is accessible (should be in PATH via inherit)
|
||||
if ! which cl.exe > /dev/null 2>&1; then
|
||||
echo "cl.exe not found, trying to locate MSVC..."
|
||||
# Find and add MSVC bin directory to PATH
|
||||
MSVC_BIN=$(cmd.exe /c "echo %VCToolsInstallDir%" | tr -d '\\r' | sed 's/\\\\\\\\/\//g' | sed 's/C:/\\/c/g')
|
||||
if [ -n "$MSVC_BIN" ]; then
|
||||
export PATH="$PATH:$MSVC_BIN/bin/Hostx64/x64:$MSVC_BIN/bin/Hostx86/x86"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run autoconf
|
||||
autoconf
|
||||
|
||||
# Configure with MSVC
|
||||
./configure CC=cl.exe CXX=cl.exe AR=lib.exe $CONFIGURE_FLAGS
|
||||
|
||||
# Build (mingw32-make is the "make" command in MSYS2)
|
||||
mingw32-make -j3
|
||||
# Build tests sequentially due to PDB file issues
|
||||
mingw32-make tests
|
||||
|
||||
# Run tests
|
||||
mingw32-make -k check
|
||||
|
||||
|
||||
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
|
|
@ -13,6 +13,8 @@
|
|||
/doc/jemalloc.html
|
||||
/doc/jemalloc.3
|
||||
|
||||
/doc_internal/PROFILING_INTERNALS.pdf
|
||||
|
||||
/jemalloc.pc
|
||||
|
||||
/lib/
|
||||
|
|
@ -30,7 +32,6 @@
|
|||
/include/jemalloc/internal/public_namespace.h
|
||||
/include/jemalloc/internal/public_symbols.txt
|
||||
/include/jemalloc/internal/public_unnamespace.h
|
||||
/include/jemalloc/internal/size_classes.h
|
||||
/include/jemalloc/jemalloc.h
|
||||
/include/jemalloc/jemalloc_defs.h
|
||||
/include/jemalloc/jemalloc_macros.h
|
||||
|
|
@ -44,6 +45,13 @@
|
|||
/src/*.[od]
|
||||
/src/*.sym
|
||||
|
||||
# These are semantically meaningful for clangd and related tooling.
|
||||
/build/
|
||||
/.cache/
|
||||
compile_commands.json
|
||||
/static_analysis_raw_results
|
||||
/static_analysis_results
|
||||
|
||||
/run_tests.out/
|
||||
|
||||
/test/test.sh
|
||||
|
|
@ -51,6 +59,7 @@ test/include/test/jemalloc_test.h
|
|||
test/include/test/jemalloc_test_defs.h
|
||||
|
||||
/test/integration/[A-Za-z]*
|
||||
!/test/integration/cpp/
|
||||
!/test/integration/[A-Za-z]*.*
|
||||
/test/integration/*.[od]
|
||||
/test/integration/*.out
|
||||
|
|
@ -64,6 +73,7 @@ test/include/test/jemalloc_test_defs.h
|
|||
|
||||
/test/stress/[A-Za-z]*
|
||||
!/test/stress/[A-Za-z]*.*
|
||||
!/test/stress/pa/
|
||||
/test/stress/*.[od]
|
||||
/test/stress/*.out
|
||||
|
||||
|
|
@ -72,6 +82,11 @@ test/include/test/jemalloc_test_defs.h
|
|||
/test/unit/*.[od]
|
||||
/test/unit/*.out
|
||||
|
||||
/test/analyze/[A-Za-z]*
|
||||
!/test/analyze/[A-Za-z]*.*
|
||||
/test/analyze/*.[od]
|
||||
/test/analyze/*.out
|
||||
|
||||
/VERSION
|
||||
|
||||
*.pdb
|
||||
|
|
|
|||
413
.travis.yml
413
.travis.yml
|
|
@ -1,156 +1,365 @@
|
|||
language: generic
|
||||
dist: precise
|
||||
# This config file is generated by ./scripts/gen_travis.py.
|
||||
# Do not edit by hand.
|
||||
|
||||
matrix:
|
||||
# We use 'minimal', because 'generic' makes Windows VMs hang at startup. Also
|
||||
# the software provided by 'generic' is simply not needed for our tests.
|
||||
# Differences are explained here:
|
||||
# https://docs.travis-ci.com/user/languages/minimal-and-generic/
|
||||
language: minimal
|
||||
dist: jammy
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: osx
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: osx
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: osx
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: osx
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: osx
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: osx
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: amd64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=clang CXX=clang++ EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-lg-hugepage=29" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-frameptr" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
# Development build
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --disable-cache-oblivious --enable-stats --enable-log --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
# --enable-expermental-smallocx:
|
||||
- os: linux
|
||||
env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-experimental-smallocx --enable-stats --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
|
||||
|
||||
|
||||
before_install:
|
||||
- |-
|
||||
if test -f "./scripts/$TRAVIS_OS_NAME/before_install.sh"; then
|
||||
source ./scripts/$TRAVIS_OS_NAME/before_install.sh
|
||||
fi
|
||||
|
||||
before_script:
|
||||
- autoconf
|
||||
- ./configure ${COMPILER_FLAGS:+ CC="$CC $COMPILER_FLAGS" CXX="$CXX $COMPILER_FLAGS" } $CONFIGURE_FLAGS
|
||||
- make -j3
|
||||
- make -j3 tests
|
||||
- |-
|
||||
if test -f "./scripts/$TRAVIS_OS_NAME/before_script.sh"; then
|
||||
source ./scripts/$TRAVIS_OS_NAME/before_script.sh
|
||||
else
|
||||
scripts/gen_travis.py > travis_script && diff .travis.yml travis_script
|
||||
autoconf
|
||||
# If COMPILER_FLAGS are not empty, add them to CC and CXX
|
||||
./configure ${COMPILER_FLAGS:+ CC="$CC $COMPILER_FLAGS" CXX="$CXX $COMPILER_FLAGS"} $CONFIGURE_FLAGS
|
||||
make -j3
|
||||
make -j3 tests
|
||||
fi
|
||||
|
||||
script:
|
||||
- make check
|
||||
- |-
|
||||
if test -f "./scripts/$TRAVIS_OS_NAME/script.sh"; then
|
||||
source ./scripts/$TRAVIS_OS_NAME/script.sh
|
||||
else
|
||||
make check
|
||||
fi
|
||||
|
||||
|
|
|
|||
4
COPYING
4
COPYING
|
|
@ -1,10 +1,10 @@
|
|||
Unless otherwise specified, files in the jemalloc source distribution are
|
||||
subject to the following license:
|
||||
--------------------------------------------------------------------------------
|
||||
Copyright (C) 2002-2018 Jason Evans <jasone@canonware.com>.
|
||||
Copyright (C) 2002-present Jason Evans <jasone@canonware.com>.
|
||||
All rights reserved.
|
||||
Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
Copyright (C) 2009-2018 Facebook, Inc. All rights reserved.
|
||||
Copyright (C) 2009-present Facebook, 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:
|
||||
|
|
|
|||
386
ChangeLog
386
ChangeLog
|
|
@ -4,7 +4,391 @@ brevity. Much more detail can be found in the git revision history:
|
|||
|
||||
https://github.com/jemalloc/jemalloc
|
||||
|
||||
* 5.1.0 (May 4th, 2018)
|
||||
* 5.3.1 (Apr 13, 2026)
|
||||
|
||||
This release includes over 390 commits spanning bug fixes, new features,
|
||||
performance optimizations, and portability improvements. Multiple percent
|
||||
of system-level metric improvements were measured in tested production
|
||||
workloads. The release has gone through large-scale production testing
|
||||
at Meta.
|
||||
|
||||
New features:
|
||||
- Support pvalloc. (@Lapenkov: 5b1f2cc5)
|
||||
- Add double free detection for the debug build. (@izaitsevfb:
|
||||
36366f3c, @guangli-dai: 42daa1ac, @divanorama: 1897f185)
|
||||
- Add compile-time option `--enable-pageid` to enable memory mapping
|
||||
annotation. (@devnexen: 4fc5c4fb)
|
||||
- Add runtime option `prof_bt_max` to control the max stack depth for
|
||||
profiling. (@guangli-dai: a0734fd6)
|
||||
- Add compile-time option `--enable-force-getenv` to use `getenv` instead
|
||||
of `secure_getenv`. (@interwq: 481bbfc9)
|
||||
- Add compile-time option `--disable-dss` to disable the usage of
|
||||
`sbrk(2)`. (@Svetlitski: ea5b7bea)
|
||||
- Add runtime option `tcache_ncached_max` to control the number of items
|
||||
in each size bin in the thread cache. (@guangli-dai: 8a22d10b)
|
||||
- Add runtime option `calloc_madvise_threshold` to determine if kernel or
|
||||
memset is used to zero the allocations for calloc. (@nullptr0-0:
|
||||
5081c16b)
|
||||
- Add compile-time option `--disable-user-config` to disable reading the
|
||||
runtime configurations from `/etc/malloc.conf` or environment variable
|
||||
`MALLOC_CONF`. (@roblabla: c17bf8b3)
|
||||
- Add runtime option `disable_large_size_classes` to guard the new usable
|
||||
size calculation, which minimizes the memory overhead for large
|
||||
allocations, i.e., >= 4 * PAGE. (@guangli-dai: c067a55c, 8347f104)
|
||||
- Enable process_madvise usage, add runtime option
|
||||
`process_madvise_max_batch` to control the max # of regions in each
|
||||
madvise batch. (@interwq: 22440a02, @spredolac: 4246475b)
|
||||
- Add mallctl interfaces:
|
||||
+ `opt.prof_bt_max` (@guangli-dai: a0734fd6)
|
||||
+ `arena.<i>.name` to set and get arena names. (@guangli-dai: ba19d2cb)
|
||||
+ `thread.tcache.max` to set and get the `tcache_max` of the current
|
||||
thread. (@guangli-dai: a442d9b8)
|
||||
+ `thread.tcache.ncached_max.write` and
|
||||
`thread.tcache.ncached_max.read_sizeclass` to set and get the
|
||||
`ncached_max` setup of the current thread. (@guangli-dai: 630f7de9,
|
||||
6b197fdd)
|
||||
+ `arenas.hugepage` to return the hugepage size used, also exported to
|
||||
malloc stats. (@ilvokhin: 90c627ed)
|
||||
+ `approximate_stats.active` to return an estimate of the current active
|
||||
bytes, which should not be compared with other stats retrieved.
|
||||
(@guangli-dai: 0988583d)
|
||||
|
||||
Bug fixes:
|
||||
- Prevent potential deadlocks in decaying during reentrancy. (@interwq:
|
||||
434a68e2)
|
||||
- Fix segfault in extent coalescing. (@Svetlitski: 12311fe6)
|
||||
- Add null pointer detections in mallctl calls. (@Svetlitski: dc0a184f,
|
||||
0288126d)
|
||||
- Make mallctl `arenas.lookup` triable without crashing on invalid
|
||||
pointers. (@auxten: 019cccc2, 5bac3849)
|
||||
- Demote sampled allocations for proper deallocations during
|
||||
`arena_reset`. (@Svetlitski: 62648c88)
|
||||
- Fix jemalloc's `read(2)` and `write(2)`. (@Svetlitski: d2c9ed3d, @lexprfuncall:
|
||||
9fdc1160)
|
||||
- Fix the pkg-config metadata file. (@BtbN: ed7e6fe7, ce8ce99a)
|
||||
- Fix the autogen.sh so that it accepts quoted extra options.
|
||||
(@honggyukim: f6fe6abd)
|
||||
- Fix `rallocx()` to set errno to ENOMEM upon OOMing. (@arter97: 38056fea,
|
||||
@interwq: 83b07578)
|
||||
- Avoid stack overflow for internal variable array usage. (@nullptr0-0:
|
||||
47c9bcd4, 48f66cf4, @xinydev: 9169e927)
|
||||
- Fix background thread initialization race. (@puzpuzpuz: 4d0ffa07)
|
||||
- Guard os_page_id against a NULL address. (@lexprfuncall: 79cc7dcc)
|
||||
- Handle tcache init failures gracefully. (@lexprfuncall: a056c20d)
|
||||
- Fix missing release of acquired neighbor edata in
|
||||
extent_try_coalesce_impl. (@spredolac: 675ab079)
|
||||
- Fix memory leak of old curr_reg on san_bump_grow_locked failure.
|
||||
(@spredolac: 5904a421)
|
||||
- Fix large alloc nrequests under-counting on cache misses. (@spredolac:
|
||||
3cc56d32)
|
||||
|
||||
Portability improvements:
|
||||
- Fix the build in C99. (@abaelhe: 56ddbea2)
|
||||
- Add `pthread_setaffinity_np` detection for non Linux/BSD platforms.
|
||||
(@devnexen: 4c95c953)
|
||||
- Make `VARIABLE_ARRAY` compatible with compilers not supporting VLA,
|
||||
i.e., Visual Studio C compiler in C11 or C17 modes. (@madscientist:
|
||||
be65438f)
|
||||
- Fix the build on Linux using musl library. (@marv: aba1645f, 45249cf5)
|
||||
- Reduce the memory overhead in small allocation sampling for systems
|
||||
with larger page sizes, e.g., ARM. (@Svetlitski: 5a858c64)
|
||||
- Add C23's `free_sized` and `free_aligned_sized`. (@Svetlitski:
|
||||
cdb2c0e0)
|
||||
- Enable heap profiling on MacOS. (@nullptr0-0: 4b555c11)
|
||||
- Fix incorrect printing on 32bit. (@sundb: 630434bb)
|
||||
- Make `JEMALLOC_CXX_THROW` compatible with C++ versions newer than
|
||||
C++17. (@r-barnes, @guangli-dai: 21bcc0a8)
|
||||
- Fix mmap tag conflicts on MacOS. (@kdrag0n: c893fcd1)
|
||||
- Fix monotonic timer assumption for win32. (@burtonli: 8dc97b11)
|
||||
- Fix VM over-reservation on systems with larger pages, e.g., aarch64.
|
||||
(@interwq: cd05b19f)
|
||||
- Remove `unreachable()` macro conditionally to prevent definition
|
||||
conflicts for C23+. (@appujee: d8486b26, 4b88bddb)
|
||||
- Fix dlsym failure observed on FreeBSD. (@rhelmot: 86bbabac)
|
||||
- Change the default page size to 64KB on aarch64 Linux. (@lexprfuncall:
|
||||
9442300c)
|
||||
- Update config.guess and config.sub to the latest version.
|
||||
(@lexprfuncall: c51949ea)
|
||||
- Determine the page size on Android from NDK header files.
|
||||
(@lexprfuncall: c51abba1)
|
||||
- Improve the portability of grep patterns in configure.ac.
|
||||
(@lexprfuncall: 365747bc)
|
||||
- Add compile-time option `--with-cxx-stdlib` to specify the C++ standard
|
||||
library. (@yuxuanchen1997: a10ef3e1)
|
||||
|
||||
Optimizations and refactors:
|
||||
- Enable tcache for deallocation-only threads. (@interwq: 143e9c4a)
|
||||
- Inline to accelerate operator delete. (@guangli-dai: e8f9f138)
|
||||
- Optimize pairing heap's performance. (@deadalnix: 5266152d, be6da4f6,
|
||||
543e2d61, 10d71315, 92aa52c0, @Svetlitski: 36ca0c1b)
|
||||
- Inline the storage for thread name in the profiling data. (@interwq:
|
||||
ce0b7ab6, e62aa478)
|
||||
- Optimize a hot function `edata_cmp_summary_comp` to accelerate it.
|
||||
(@Svetlitski: 6841110b, @guangli-dai: 0181aaa4)
|
||||
- Allocate thread cache using the base allocator, which enables thread
|
||||
cache to use thp when `metadata_thp` is turned on. (@interwq:
|
||||
72cfdce7)
|
||||
- Allow oversize arena not to purge immediately when background threads
|
||||
are enabled, although the default decay time is 0 to be back compatible.
|
||||
(@interwq: d1313313)
|
||||
- Optimize thread-local storage implementation on Windows. (@mcfi:
|
||||
9e123a83, 3a0d9cda)
|
||||
- Optimize fast path to allow static size class computation. (@interwq:
|
||||
323ed2e3)
|
||||
- Redesign tcache GC to regulate the frequency and make it
|
||||
locality-aware. The new design is default on, guarded by option
|
||||
`experimental_tcache_gc`. (@nullptr0-0: 0c88be9e, e2c9f3a9,
|
||||
14d5dc13, @deadalnix: 5afff2e4)
|
||||
- Reduce the arena switching overhead by avoiding forced purging when
|
||||
background thread is enabled. (@interwq: a3910b98)
|
||||
- Improve the reuse efficiency by limiting the maximum coalesced size for
|
||||
large extents. (@jiebinn: 3c14707b)
|
||||
- Refactor thread events to allow registration of users' thread events
|
||||
and remove prof_threshold as the built-in event. (@spredolac: e6864c60,
|
||||
015b0179, 34ace916)
|
||||
|
||||
Documentation:
|
||||
- Update Windows building instructions. (@Lapenkov: 37139328)
|
||||
- Add vcpkg installation instructions. (@LilyWangLL: c0c9783e)
|
||||
- Update profiling internals with an example. (@jordalgo: b04e7666)
|
||||
|
||||
* 5.3.0 (May 6, 2022)
|
||||
|
||||
This release contains many speed and space optimizations, from micro
|
||||
optimizations on common paths to rework of internal data structures and
|
||||
locking schemes, and many more too detailed to list below. Multiple percent
|
||||
of system level metric improvements were measured in tested production
|
||||
workloads. The release has gone through large-scale production testing.
|
||||
|
||||
New features:
|
||||
- Add the thread.idle mallctl which hints that the calling thread will be
|
||||
idle for a nontrivial period of time. (@davidtgoldblatt)
|
||||
- Allow small size classes to be the maximum size class to cache in the
|
||||
thread-specific cache, through the opt.[lg_]tcache_max option. (@interwq,
|
||||
@jordalgo)
|
||||
- Make the behavior of realloc(ptr, 0) configurable with opt.zero_realloc.
|
||||
(@davidtgoldblatt)
|
||||
- Add 'make uninstall' support. (@sangshuduo, @Lapenkov)
|
||||
- Support C++17 over-aligned allocation. (@marksantaniello)
|
||||
- Add the thread.peak mallctl for approximate per-thread peak memory tracking.
|
||||
(@davidtgoldblatt)
|
||||
- Add interval-based stats output opt.stats_interval. (@interwq)
|
||||
- Add prof.prefix to override filename prefixes for dumps. (@zhxchen17)
|
||||
- Add high resolution timestamp support for profiling. (@tyroguru)
|
||||
- Add the --collapsed flag to jeprof for flamegraph generation.
|
||||
(@igorwwwwwwwwwwwwwwwwwwww)
|
||||
- Add the --debug-syms-by-id option to jeprof for debug symbols discovery.
|
||||
(@DeannaGelbart)
|
||||
- Add the opt.prof_leak_error option to exit with error code when leak is
|
||||
detected using opt.prof_final. (@yunxuo)
|
||||
- Add opt.cache_oblivious as an runtime alternative to config.cache_oblivious.
|
||||
(@interwq)
|
||||
- Add mallctl interfaces:
|
||||
+ opt.zero_realloc (@davidtgoldblatt)
|
||||
+ opt.cache_oblivious (@interwq)
|
||||
+ opt.prof_leak_error (@yunxuo)
|
||||
+ opt.stats_interval (@interwq)
|
||||
+ opt.stats_interval_opts (@interwq)
|
||||
+ opt.tcache_max (@interwq)
|
||||
+ opt.trust_madvise (@azat)
|
||||
+ prof.prefix (@zhxchen17)
|
||||
+ stats.zero_reallocs (@davidtgoldblatt)
|
||||
+ thread.idle (@davidtgoldblatt)
|
||||
+ thread.peak.{read,reset} (@davidtgoldblatt)
|
||||
|
||||
Bug fixes:
|
||||
- Fix the synchronization around explicit tcache creation which could cause
|
||||
invalid tcache identifiers. This regression was first released in 5.0.0.
|
||||
(@yoshinorim, @davidtgoldblatt)
|
||||
- Fix a profiling biasing issue which could cause incorrect heap usage and
|
||||
object counts. This issue existed in all previous releases with the heap
|
||||
profiling feature. (@davidtgoldblatt)
|
||||
- Fix the order of stats counter updating on large realloc which could cause
|
||||
failed assertions. This regression was first released in 5.0.0. (@azat)
|
||||
- Fix the locking on the arena destroy mallctl, which could cause concurrent
|
||||
arena creations to fail. This functionality was first introduced in 5.0.0.
|
||||
(@interwq)
|
||||
|
||||
Portability improvements:
|
||||
- Remove nothrow from system function declarations on macOS and FreeBSD.
|
||||
(@davidtgoldblatt, @fredemmott, @leres)
|
||||
- Improve overcommit and page alignment settings on NetBSD. (@zoulasc)
|
||||
- Improve CPU affinity support on BSD platforms. (@devnexen)
|
||||
- Improve utrace detection and support. (@devnexen)
|
||||
- Improve QEMU support with MADV_DONTNEED zeroed pages detection. (@azat)
|
||||
- Add memcntl support on Solaris / illumos. (@devnexen)
|
||||
- Improve CPU_SPINWAIT on ARM. (@AWSjswinney)
|
||||
- Improve TSD cleanup on FreeBSD. (@Lapenkov)
|
||||
- Disable percpu_arena if the CPU count cannot be reliably detected. (@azat)
|
||||
- Add malloc_size(3) override support. (@devnexen)
|
||||
- Add mmap VM_MAKE_TAG support. (@devnexen)
|
||||
- Add support for MADV_[NO]CORE. (@devnexen)
|
||||
- Add support for DragonFlyBSD. (@devnexen)
|
||||
- Fix the QUANTUM setting on MIPS64. (@brooksdavis)
|
||||
- Add the QUANTUM setting for ARC. (@vineetgarc)
|
||||
- Add the QUANTUM setting for LoongArch. (@wangjl-uos)
|
||||
- Add QNX support. (@jqian-aurora)
|
||||
- Avoid atexit(3) calls unless the relevant profiling features are enabled.
|
||||
(@BusyJay, @laiwei-rice, @interwq)
|
||||
- Fix unknown option detection when using Clang. (@Lapenkov)
|
||||
- Fix symbol conflict with musl libc. (@georgthegreat)
|
||||
- Add -Wimplicit-fallthrough checks. (@nickdesaulniers)
|
||||
- Add __forceinline support on MSVC. (@santagada)
|
||||
- Improve FreeBSD and Windows CI support. (@Lapenkov)
|
||||
- Add CI support for PPC64LE architecture. (@ezeeyahoo)
|
||||
|
||||
Incompatible changes:
|
||||
- Maximum size class allowed in tcache (opt.[lg_]tcache_max) now has an upper
|
||||
bound of 8MiB. (@interwq)
|
||||
|
||||
Optimizations and refactors (@davidtgoldblatt, @Lapenkov, @interwq):
|
||||
- Optimize the common cases of the thread cache operations.
|
||||
- Optimize internal data structures, including RB tree and pairing heap.
|
||||
- Optimize the internal locking on extent management.
|
||||
- Extract and refactor the internal page allocator and interface modules.
|
||||
|
||||
Documentation:
|
||||
- Fix doc build with --with-install-suffix. (@lawmurray, @interwq)
|
||||
- Add PROFILING_INTERNALS.md. (@davidtgoldblatt)
|
||||
- Ensure the proper order of doc building and installation. (@Mingli-Yu)
|
||||
|
||||
* 5.2.1 (August 5, 2019)
|
||||
|
||||
This release is primarily about Windows. A critical virtual memory leak is
|
||||
resolved on all Windows platforms. The regression was present in all releases
|
||||
since 5.0.0.
|
||||
|
||||
Bug fixes:
|
||||
- Fix a severe virtual memory leak on Windows. This regression was first
|
||||
released in 5.0.0. (@Ignition, @j0t, @frederik-h, @davidtgoldblatt,
|
||||
@interwq)
|
||||
- Fix size 0 handling in posix_memalign(). This regression was first released
|
||||
in 5.2.0. (@interwq)
|
||||
- Fix the prof_log unit test which may observe unexpected backtraces from
|
||||
compiler optimizations. The test was first added in 5.2.0. (@marxin,
|
||||
@gnzlbg, @interwq)
|
||||
- Fix the declaration of the extent_avail tree. This regression was first
|
||||
released in 5.1.0. (@zoulasc)
|
||||
- Fix an incorrect reference in jeprof. This functionality was first released
|
||||
in 3.0.0. (@prehistoric-penguin)
|
||||
- Fix an assertion on the deallocation fast-path. This regression was first
|
||||
released in 5.2.0. (@yinan1048576)
|
||||
- Fix the TLS_MODEL attribute in headers. This regression was first released
|
||||
in 5.0.0. (@zoulasc, @interwq)
|
||||
|
||||
Optimizations and refactors:
|
||||
- Implement opt.retain on Windows and enable by default on 64-bit. (@interwq,
|
||||
@davidtgoldblatt)
|
||||
- Optimize away a branch on the operator delete[] path. (@mgrice)
|
||||
- Add format annotation to the format generator function. (@zoulasc)
|
||||
- Refactor and improve the size class header generation. (@yinan1048576)
|
||||
- Remove best fit. (@djwatson)
|
||||
- Avoid blocking on background thread locks for stats. (@oranagra, @interwq)
|
||||
|
||||
* 5.2.0 (April 2, 2019)
|
||||
|
||||
This release includes a few notable improvements, which are summarized below:
|
||||
1) improved fast-path performance from the optimizations by @djwatson; 2)
|
||||
reduced virtual memory fragmentation and metadata usage; and 3) bug fixes on
|
||||
setting the number of background threads. In addition, peak / spike memory
|
||||
usage is improved with certain allocation patterns. As usual, the release and
|
||||
prior dev versions have gone through large-scale production testing.
|
||||
|
||||
New features:
|
||||
- Implement oversize_threshold, which uses a dedicated arena for allocations
|
||||
crossing the specified threshold to reduce fragmentation. (@interwq)
|
||||
- Add extents usage information to stats. (@tyleretzel)
|
||||
- Log time information for sampled allocations. (@tyleretzel)
|
||||
- Support 0 size in sdallocx. (@djwatson)
|
||||
- Output rate for certain counters in malloc_stats. (@zinoale)
|
||||
- Add configure option --enable-readlinkat, which allows the use of readlinkat
|
||||
over readlink. (@davidtgoldblatt)
|
||||
- Add configure options --{enable,disable}-{static,shared} to allow not
|
||||
building unwanted libraries. (@Ericson2314)
|
||||
- Add configure option --disable-libdl to enable fully static builds.
|
||||
(@interwq)
|
||||
- Add mallctl interfaces:
|
||||
+ opt.oversize_threshold (@interwq)
|
||||
+ stats.arenas.<i>.extent_avail (@tyleretzel)
|
||||
+ stats.arenas.<i>.extents.<j>.n{dirty,muzzy,retained} (@tyleretzel)
|
||||
+ stats.arenas.<i>.extents.<j>.{dirty,muzzy,retained}_bytes
|
||||
(@tyleretzel)
|
||||
|
||||
Portability improvements:
|
||||
- Update MSVC builds. (@maksqwe, @rustyx)
|
||||
- Workaround a compiler optimizer bug on s390x. (@rkmisra)
|
||||
- Make use of pthread_set_name_np(3) on FreeBSD. (@trasz)
|
||||
- Implement malloc_getcpu() to enable percpu_arena for windows. (@santagada)
|
||||
- Link against -pthread instead of -lpthread. (@paravoid)
|
||||
- Make background_thread not dependent on libdl. (@interwq)
|
||||
- Add stringify to fix a linker directive issue on MSVC. (@daverigby)
|
||||
- Detect and fall back when 8-bit atomics are unavailable. (@interwq)
|
||||
- Fall back to the default pthread_create if dlsym(3) fails. (@interwq)
|
||||
|
||||
Optimizations and refactors:
|
||||
- Refactor the TSD module. (@davidtgoldblatt)
|
||||
- Avoid taking extents_muzzy mutex when muzzy is disabled. (@interwq)
|
||||
- Avoid taking large_mtx for auto arenas on the tcache flush path. (@interwq)
|
||||
- Optimize ixalloc by avoiding a size lookup. (@interwq)
|
||||
- Implement opt.oversize_threshold which uses a dedicated arena for requests
|
||||
crossing the threshold, also eagerly purges the oversize extents. Default
|
||||
the threshold to 8 MiB. (@interwq)
|
||||
- Clean compilation with -Wextra. (@gnzlbg, @jasone)
|
||||
- Refactor the size class module. (@davidtgoldblatt)
|
||||
- Refactor the stats emitter. (@tyleretzel)
|
||||
- Optimize pow2_ceil. (@rkmisra)
|
||||
- Avoid runtime detection of lazy purging on FreeBSD. (@trasz)
|
||||
- Optimize mmap(2) alignment handling on FreeBSD. (@trasz)
|
||||
- Improve error handling for THP state initialization. (@jsteemann)
|
||||
- Rework the malloc() fast path. (@djwatson)
|
||||
- Rework the free() fast path. (@djwatson)
|
||||
- Refactor and optimize the tcache fill / flush paths. (@djwatson)
|
||||
- Optimize sync / lwsync on PowerPC. (@chmeeedalf)
|
||||
- Bypass extent_dalloc() when retain is enabled. (@interwq)
|
||||
- Optimize the locking on large deallocation. (@interwq)
|
||||
- Reduce the number of pages committed from sanity checking in debug build.
|
||||
(@trasz, @interwq)
|
||||
- Deprecate OSSpinLock. (@interwq)
|
||||
- Lower the default number of background threads to 4 (when the feature
|
||||
is enabled). (@interwq)
|
||||
- Optimize the trylock spin wait. (@djwatson)
|
||||
- Use arena index for arena-matching checks. (@interwq)
|
||||
- Avoid forced decay on thread termination when using background threads.
|
||||
(@interwq)
|
||||
- Disable muzzy decay by default. (@djwatson, @interwq)
|
||||
- Only initialize libgcc unwinder when profiling is enabled. (@paravoid,
|
||||
@interwq)
|
||||
|
||||
Bug fixes (all only relevant to jemalloc 5.x):
|
||||
- Fix background thread index issues with max_background_threads. (@djwatson,
|
||||
@interwq)
|
||||
- Fix stats output for opt.lg_extent_max_active_fit. (@interwq)
|
||||
- Fix opt.prof_prefix initialization. (@davidtgoldblatt)
|
||||
- Properly trigger decay on tcache destroy. (@interwq, @amosbird)
|
||||
- Fix tcache.flush. (@interwq)
|
||||
- Detect whether explicit extent zero out is necessary with huge pages or
|
||||
custom extent hooks, which may change the purge semantics. (@interwq)
|
||||
- Fix a side effect caused by extent_max_active_fit combined with decay-based
|
||||
purging, where freed extents can accumulate and not be reused for an
|
||||
extended period of time. (@interwq, @mpghf)
|
||||
- Fix a missing unlock on extent register error handling. (@zoulasc)
|
||||
|
||||
Testing:
|
||||
- Simplify the Travis script output. (@gnzlbg)
|
||||
- Update the test scripts for FreeBSD. (@devnexen)
|
||||
- Add unit tests for the producer-consumer pattern. (@interwq)
|
||||
- Add Cirrus-CI config for FreeBSD builds. (@jasone)
|
||||
- Add size-matching sanity checks on tcache flush. (@davidtgoldblatt,
|
||||
@interwq)
|
||||
|
||||
Incompatible changes:
|
||||
- Remove --with-lg-page-sizes. (@davidtgoldblatt)
|
||||
|
||||
Documentation:
|
||||
- Attempt to build docs by default, however skip doc building when xsltproc
|
||||
is missing. (@interwq, @cmuellner)
|
||||
|
||||
* 5.1.0 (May 4, 2018)
|
||||
|
||||
This release is primarily about fine-tuning, ranging from several new features
|
||||
to numerous notable performance and portability enhancements. The release and
|
||||
|
|
|
|||
154
INSTALL.md
154
INSTALL.md
|
|
@ -9,14 +9,17 @@ If building from unpackaged developer sources, the simplest command sequence
|
|||
that might work is:
|
||||
|
||||
./autogen.sh
|
||||
make dist
|
||||
make
|
||||
make install
|
||||
|
||||
Note that documentation is not built by the default target because doing so
|
||||
would create a dependency on xsltproc in packaged releases, hence the
|
||||
requirement to either run 'make dist' or avoid installing docs via the various
|
||||
install_* targets documented below.
|
||||
You can uninstall the installed build artifacts like this:
|
||||
|
||||
make uninstall
|
||||
|
||||
Notes:
|
||||
- "autoconf" needs to be installed
|
||||
- Documentation is built by the default target only when xsltproc is
|
||||
available. Build will warn but not stop if the dependency is missing.
|
||||
|
||||
|
||||
## Advanced configuration
|
||||
|
|
@ -136,6 +139,7 @@ any of the following arguments (not a definitive list) to 'configure':
|
|||
in the following list that appears to function correctly:
|
||||
|
||||
+ libunwind (requires --enable-prof-libunwind)
|
||||
+ frame pointer (requires --enable-prof-frameptr)
|
||||
+ libgcc (unless --disable-prof-libgcc)
|
||||
+ gcc intrinsics (unless --disable-prof-gcc)
|
||||
|
||||
|
|
@ -144,6 +148,12 @@ any of the following arguments (not a definitive list) to 'configure':
|
|||
Use the libunwind library (http://www.nongnu.org/libunwind/) for stack
|
||||
backtracing.
|
||||
|
||||
* `--enable-prof-frameptr`
|
||||
|
||||
Use the optimized frame pointer unwinder for stack backtracing. Safe
|
||||
to use in mixed code (with and without frame pointers) - but requires
|
||||
frame pointers to produce meaningful stacks. Linux only.
|
||||
|
||||
* `--disable-prof-libgcc`
|
||||
|
||||
Disable the use of libgcc's backtracing functionality.
|
||||
|
|
@ -188,13 +198,13 @@ any of the following arguments (not a definitive list) to 'configure':
|
|||
|
||||
* `--disable-cache-oblivious`
|
||||
|
||||
Disable cache-oblivious large allocation alignment for large allocation
|
||||
requests with no alignment constraints. If this feature is disabled, all
|
||||
large allocations are page-aligned as an implementation artifact, which can
|
||||
severely harm CPU cache utilization. However, the cache-oblivious layout
|
||||
comes at the cost of one extra page per large allocation, which in the
|
||||
most extreme case increases physical memory usage for the 16 KiB size class
|
||||
to 20 KiB.
|
||||
Disable cache-oblivious large allocation alignment by default, for large
|
||||
allocation requests with no alignment constraints. If this feature is
|
||||
disabled, all large allocations are page-aligned as an implementation
|
||||
artifact, which can severely harm CPU cache utilization. However, the
|
||||
cache-oblivious layout comes at the cost of one extra page per large
|
||||
allocation, which in the most extreme case increases physical memory usage
|
||||
for the 16 KiB size class to 20 KiB.
|
||||
|
||||
* `--disable-syscall`
|
||||
|
||||
|
|
@ -221,13 +231,6 @@ any of the following arguments (not a definitive list) to 'configure':
|
|||
system page size may change between configuration and execution, e.g. when
|
||||
cross compiling.
|
||||
|
||||
* `--with-lg-page-sizes=<lg-page-sizes>`
|
||||
|
||||
Specify the comma-separated base 2 logs of the page sizes to support. This
|
||||
option may be useful when cross compiling in combination with
|
||||
`--with-lg-page`, but its primary use case is for integration with FreeBSD's
|
||||
libc, wherein jemalloc is embedded.
|
||||
|
||||
* `--with-lg-hugepage=<lg-hugepage>`
|
||||
|
||||
Specify the base 2 log of the system huge page size. This option is useful
|
||||
|
|
@ -276,6 +279,11 @@ any of the following arguments (not a definitive list) to 'configure':
|
|||
in the same process, which will almost certainly result in confusing runtime
|
||||
crashes if pointers leak from one implementation to the other.
|
||||
|
||||
* `--disable-libdl`
|
||||
|
||||
Disable the usage of libdl, namely dlsym(3) which is required by the lazy
|
||||
lock option. This can allow building static binaries.
|
||||
|
||||
The following environment variables (not a definitive list) impact configure's
|
||||
behavior:
|
||||
|
||||
|
|
@ -314,13 +322,13 @@ behavior:
|
|||
'configure' uses this to find programs.
|
||||
|
||||
In some cases it may be necessary to work around configuration results that do
|
||||
not match reality. For example, Linux 4.5 added support for the MADV_FREE flag
|
||||
to madvise(2), which can cause problems if building on a host with MADV_FREE
|
||||
support and deploying to a target without. To work around this, use a cache
|
||||
file to override the relevant configuration variable defined in configure.ac,
|
||||
e.g.:
|
||||
not match reality. For example, Linux 3.4 added support for the MADV_DONTDUMP
|
||||
flag to madvise(2), which can cause problems if building on a host with
|
||||
MADV_DONTDUMP support and deploying to a target without. To work around this,
|
||||
use a cache file to override the relevant configuration variable defined in
|
||||
configure.ac, e.g.:
|
||||
|
||||
echo "je_cv_madv_free=no" > config.cache && ./configure -C
|
||||
echo "je_cv_madv_dontdump=no" > config.cache && ./configure -C
|
||||
|
||||
|
||||
## Advanced compilation
|
||||
|
|
@ -395,6 +403,102 @@ exclusively):
|
|||
|
||||
Use this to search for programs used during configuration and building.
|
||||
|
||||
## Building for Windows
|
||||
|
||||
There are at least two ways to build jemalloc's libraries for Windows. They
|
||||
differ in their ease of use and flexibility.
|
||||
|
||||
### With MSVC solutions
|
||||
This is the easy, but less flexible approach. It doesn't let you specify
|
||||
arguments to the `configure` script.
|
||||
|
||||
1. Install Cygwin with at least the following packages:
|
||||
* autoconf
|
||||
* autogen
|
||||
* gawk
|
||||
* grep
|
||||
* sed
|
||||
|
||||
2. Install Visual Studio 2015 or 2017 with Visual C++
|
||||
|
||||
3. Add Cygwin\bin to the PATH environment variable
|
||||
|
||||
4. Open "x64 Native Tools Command Prompt for VS 2017"
|
||||
(note: x86/x64 doesn't matter at this point)
|
||||
|
||||
5. Generate header files:
|
||||
sh -c "CC=cl ./autogen.sh"
|
||||
|
||||
6. Now the project can be opened and built in Visual Studio:
|
||||
msvc\jemalloc_vc2017.sln
|
||||
|
||||
### With MSYS
|
||||
This is a more involved approach that offers the same configuration flexibility
|
||||
as Linux builds. We use it for our CI workflow to test different jemalloc
|
||||
configurations on Windows.
|
||||
|
||||
1. Install the prerequisites
|
||||
1. MSYS2
|
||||
2. Chocolatey
|
||||
3. Visual Studio if you want to compile with MSVC compiler
|
||||
|
||||
2. Run your bash emulation. It could be MSYS2 or Git Bash (this manual was
|
||||
tested on both)
|
||||
3. Manually and selectively follow
|
||||
[before_install.sh](https://github.com/jemalloc/jemalloc/blob/dev/scripts/windows/before_install.sh)
|
||||
script.
|
||||
1. Skip the `TRAVIS_OS_NAME` check, `rm -rf C:/tools/msys64` and `choco
|
||||
uninstall/upgrade` part.
|
||||
2. If using `msys2` shell, add path to `RefreshEnv.cmd` to `PATH`:
|
||||
`PATH="$PATH:/c/ProgramData/chocolatey/bin"`
|
||||
3. Assign `msys_shell_cmd`, `msys2`, `mingw32` and `mingw64` as in the
|
||||
script.
|
||||
4. Pick `CROSS_COMPILE_32BIT` , `CC` and `USE_MSVC` values depending on
|
||||
your needs. For instance, if you'd like to build for x86_64 Windows
|
||||
with `gcc`, then `CROSS_COMPILE_32BIT="no"`, `CC="gcc"` and
|
||||
`USE_MSVC=""`. If you'd like to build for x86 Windows with `cl.exe`,
|
||||
then `CROSS_COMPILE_32BIT="yes"`, `CC="cl.exe"`, `USE_MSVC="x86"`.
|
||||
For x86_64 builds with `cl.exe`, assign `USE_MSVC="amd64"` and
|
||||
`CROSS_COMPILE_32BIT="no"`.
|
||||
5. Replace the path to `vcvarsall.bat` with the path on your system. For
|
||||
instance, on my Windows PC with Visual Studio 17, the path is
|
||||
`C:\Program Files (x86)\Microsoft Visual
|
||||
Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat`.
|
||||
6. Execute the rest of the script. It will install the required
|
||||
dependencies and assign the variable `build_env`, which is a function
|
||||
that executes following commands with the correct environment
|
||||
variables set.
|
||||
4. Use `$build_env <command>` as you would in a Linux shell:
|
||||
1. `$build_env autoconf`
|
||||
2. `$build_env ./configure CC="<desired compiler>" <configuration flags>`
|
||||
3. `$build_env mingw32-make`
|
||||
|
||||
If you're having any issues with the above, ensure the following:
|
||||
|
||||
5. When you run `cmd //C RefreshEnv.cmd`, you get an output line starting with
|
||||
`Refreshing` . If it errors saying `RefreshEnv.cmd` is not found, then you
|
||||
need to add it to your `PATH` as described above in item 3.2
|
||||
|
||||
6. When you run `cmd //C $vcvarsall`, it prints a bunch of environment
|
||||
variables. Otherwise, check the path to the `vcvarsall.bat` in `$vcvarsall`
|
||||
script and fix it.
|
||||
|
||||
### Building from vcpkg
|
||||
|
||||
The jemalloc port in vcpkg is kept up to date by Microsoft team members and
|
||||
community contributors. The url of vcpkg is: https://github.com/Microsoft/vcpkg
|
||||
. You can download and install jemalloc using the vcpkg dependency manager:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh # ./bootstrap-vcpkg.bat for Windows
|
||||
./vcpkg integrate install
|
||||
./vcpkg install jemalloc
|
||||
```
|
||||
|
||||
If the version is out of date, please [create an issue or pull
|
||||
request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||
|
||||
## Development
|
||||
|
||||
|
|
|
|||
315
Makefile.in
315
Makefile.in
|
|
@ -47,6 +47,7 @@ REV := @rev@
|
|||
install_suffix := @install_suffix@
|
||||
ABI := @abi@
|
||||
XSLTPROC := @XSLTPROC@
|
||||
XSLROOT := @XSLROOT@
|
||||
AUTOCONF := @AUTOCONF@
|
||||
_RPATH = @RPATH@
|
||||
RPATH = $(if $(1),$(call _RPATH,$(1)))
|
||||
|
|
@ -55,8 +56,12 @@ cfghdrs_out := @cfghdrs_out@
|
|||
cfgoutputs_in := $(addprefix $(srcroot),@cfgoutputs_in@)
|
||||
cfgoutputs_out := @cfgoutputs_out@
|
||||
enable_autogen := @enable_autogen@
|
||||
enable_doc := @enable_doc@
|
||||
enable_shared := @enable_shared@
|
||||
enable_static := @enable_static@
|
||||
enable_prof := @enable_prof@
|
||||
enable_zone_allocator := @enable_zone_allocator@
|
||||
enable_experimental_smallocx := @enable_experimental_smallocx@
|
||||
MALLOC_CONF := @JEMALLOC_CPREFIX@MALLOC_CONF
|
||||
link_whole_archive := @link_whole_archive@
|
||||
DSO_LDFLAGS = @DSO_LDFLAGS@
|
||||
|
|
@ -94,30 +99,67 @@ C_SRCS := $(srcroot)src/jemalloc.c \
|
|||
$(srcroot)src/background_thread.c \
|
||||
$(srcroot)src/base.c \
|
||||
$(srcroot)src/bin.c \
|
||||
$(srcroot)src/bin_info.c \
|
||||
$(srcroot)src/bitmap.c \
|
||||
$(srcroot)src/buf_writer.c \
|
||||
$(srcroot)src/cache_bin.c \
|
||||
$(srcroot)src/ckh.c \
|
||||
$(srcroot)src/counter.c \
|
||||
$(srcroot)src/ctl.c \
|
||||
$(srcroot)src/decay.c \
|
||||
$(srcroot)src/div.c \
|
||||
$(srcroot)src/ecache.c \
|
||||
$(srcroot)src/edata.c \
|
||||
$(srcroot)src/edata_cache.c \
|
||||
$(srcroot)src/ehooks.c \
|
||||
$(srcroot)src/emap.c \
|
||||
$(srcroot)src/eset.c \
|
||||
$(srcroot)src/exp_grow.c \
|
||||
$(srcroot)src/extent.c \
|
||||
$(srcroot)src/extent_dss.c \
|
||||
$(srcroot)src/extent_mmap.c \
|
||||
$(srcroot)src/hash.c \
|
||||
$(srcroot)src/hooks.c \
|
||||
$(srcroot)src/fxp.c \
|
||||
$(srcroot)src/san.c \
|
||||
$(srcroot)src/san_bump.c \
|
||||
$(srcroot)src/hook.c \
|
||||
$(srcroot)src/hpa.c \
|
||||
$(srcroot)src/hpa_central.c \
|
||||
$(srcroot)src/hpa_hooks.c \
|
||||
$(srcroot)src/hpa_utils.c \
|
||||
$(srcroot)src/hpdata.c \
|
||||
$(srcroot)src/inspect.c \
|
||||
$(srcroot)src/large.c \
|
||||
$(srcroot)src/log.c \
|
||||
$(srcroot)src/malloc_io.c \
|
||||
$(srcroot)src/conf.c \
|
||||
$(srcroot)src/mutex.c \
|
||||
$(srcroot)src/mutex_pool.c \
|
||||
$(srcroot)src/nstime.c \
|
||||
$(srcroot)src/pa.c \
|
||||
$(srcroot)src/pa_extra.c \
|
||||
$(srcroot)src/pac.c \
|
||||
$(srcroot)src/pages.c \
|
||||
$(srcroot)src/prng.c \
|
||||
$(srcroot)src/peak_event.c \
|
||||
$(srcroot)src/prof.c \
|
||||
$(srcroot)src/prof_data.c \
|
||||
$(srcroot)src/prof_log.c \
|
||||
$(srcroot)src/prof_recent.c \
|
||||
$(srcroot)src/prof_stack_range.c \
|
||||
$(srcroot)src/prof_stats.c \
|
||||
$(srcroot)src/prof_sys.c \
|
||||
$(srcroot)src/psset.c \
|
||||
$(srcroot)src/rtree.c \
|
||||
$(srcroot)src/safety_check.c \
|
||||
$(srcroot)src/sc.c \
|
||||
$(srcroot)src/sec.c \
|
||||
$(srcroot)src/stats.c \
|
||||
$(srcroot)src/sz.c \
|
||||
$(srcroot)src/tcache.c \
|
||||
$(srcroot)src/test_hooks.c \
|
||||
$(srcroot)src/thread_event.c \
|
||||
$(srcroot)src/thread_event_registry.c \
|
||||
$(srcroot)src/ticker.c \
|
||||
$(srcroot)src/tsd.c \
|
||||
$(srcroot)src/util.c \
|
||||
$(srcroot)src/witness.c
|
||||
ifeq ($(enable_zone_allocator), 1)
|
||||
C_SRCS += $(srcroot)src/zone.c
|
||||
|
|
@ -140,102 +182,186 @@ else
|
|||
LJEMALLOC := $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
|
||||
endif
|
||||
PC := $(objroot)jemalloc.pc
|
||||
MAN3 := $(objroot)doc/jemalloc$(install_suffix).3
|
||||
DOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml
|
||||
DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.html)
|
||||
DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.3)
|
||||
DOCS := $(DOCS_HTML) $(DOCS_MAN3)
|
||||
C_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \
|
||||
$(srcroot)test/src/btalloc_1.c $(srcroot)test/src/math.c \
|
||||
$(srcroot)test/src/mtx.c $(srcroot)test/src/mq.c \
|
||||
$(srcroot)test/src/mtx.c $(srcroot)test/src/sleep.c \
|
||||
$(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \
|
||||
$(srcroot)test/src/thd.c $(srcroot)test/src/timer.c
|
||||
ifeq (1, $(link_whole_archive))
|
||||
C_UTIL_INTEGRATION_SRCS :=
|
||||
C_UTIL_CPP_SRCS :=
|
||||
else
|
||||
C_UTIL_INTEGRATION_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c
|
||||
C_UTIL_INTEGRATION_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c \
|
||||
$(srcroot)src/ticker.c
|
||||
C_UTIL_CPP_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c
|
||||
endif
|
||||
TESTS_UNIT := \
|
||||
$(srcroot)test/unit/a0.c \
|
||||
$(srcroot)test/unit/arena_decay.c \
|
||||
$(srcroot)test/unit/arena_reset.c \
|
||||
$(srcroot)test/unit/atomic.c \
|
||||
$(srcroot)test/unit/background_thread.c \
|
||||
$(srcroot)test/unit/background_thread_enable.c \
|
||||
$(srcroot)test/unit/background_thread_init.c \
|
||||
$(srcroot)test/unit/base.c \
|
||||
$(srcroot)test/unit/batch_alloc.c \
|
||||
$(srcroot)test/unit/bin.c \
|
||||
$(srcroot)test/unit/binshard.c \
|
||||
$(srcroot)test/unit/bitmap.c \
|
||||
$(srcroot)test/unit/bit_util.c \
|
||||
$(srcroot)test/unit/buf_writer.c \
|
||||
$(srcroot)test/unit/cache_bin.c \
|
||||
$(srcroot)test/unit/ckh.c \
|
||||
$(srcroot)test/unit/conf.c \
|
||||
$(srcroot)test/unit/conf_init_0.c \
|
||||
$(srcroot)test/unit/conf_init_1.c \
|
||||
$(srcroot)test/unit/conf_init_confirm.c \
|
||||
$(srcroot)test/unit/conf_parse.c \
|
||||
$(srcroot)test/unit/counter.c \
|
||||
$(srcroot)test/unit/decay.c \
|
||||
$(srcroot)test/unit/div.c \
|
||||
$(srcroot)test/unit/double_free.c \
|
||||
$(srcroot)test/unit/edata_cache.c \
|
||||
$(srcroot)test/unit/emitter.c \
|
||||
$(srcroot)test/unit/extent_quantize.c \
|
||||
${srcroot}test/unit/fb.c \
|
||||
$(srcroot)test/unit/fork.c \
|
||||
${srcroot}test/unit/fxp.c \
|
||||
${srcroot}test/unit/san.c \
|
||||
${srcroot}test/unit/san_bump.c \
|
||||
$(srcroot)test/unit/hash.c \
|
||||
$(srcroot)test/unit/hooks.c \
|
||||
$(srcroot)test/unit/hook.c \
|
||||
$(srcroot)test/unit/hpa.c \
|
||||
$(srcroot)test/unit/hpa_sec_integration.c \
|
||||
$(srcroot)test/unit/hpa_thp_always.c \
|
||||
$(srcroot)test/unit/hpa_vectorized_madvise.c \
|
||||
$(srcroot)test/unit/hpa_vectorized_madvise_large_batch.c \
|
||||
$(srcroot)test/unit/hpa_background_thread.c \
|
||||
$(srcroot)test/unit/hpdata.c \
|
||||
$(srcroot)test/unit/huge.c \
|
||||
$(srcroot)test/unit/inspect.c \
|
||||
$(srcroot)test/unit/junk.c \
|
||||
$(srcroot)test/unit/junk_alloc.c \
|
||||
$(srcroot)test/unit/junk_free.c \
|
||||
$(srcroot)test/unit/json_stats.c \
|
||||
$(srcroot)test/unit/large_ralloc.c \
|
||||
$(srcroot)test/unit/log.c \
|
||||
$(srcroot)test/unit/mallctl.c \
|
||||
$(srcroot)test/unit/malloc_conf_2.c \
|
||||
$(srcroot)test/unit/malloc_io.c \
|
||||
$(srcroot)test/unit/math.c \
|
||||
$(srcroot)test/unit/mpsc_queue.c \
|
||||
$(srcroot)test/unit/mq.c \
|
||||
$(srcroot)test/unit/mtx.c \
|
||||
$(srcroot)test/unit/nstime.c \
|
||||
$(srcroot)test/unit/ncached_max.c \
|
||||
$(srcroot)test/unit/oversize_threshold.c \
|
||||
$(srcroot)test/unit/pa.c \
|
||||
$(srcroot)test/unit/pack.c \
|
||||
$(srcroot)test/unit/pages.c \
|
||||
$(srcroot)test/unit/peak.c \
|
||||
$(srcroot)test/unit/ph.c \
|
||||
$(srcroot)test/unit/prng.c \
|
||||
$(srcroot)test/unit/prof_accum.c \
|
||||
$(srcroot)test/unit/prof_active.c \
|
||||
$(srcroot)test/unit/prof_gdump.c \
|
||||
$(srcroot)test/unit/prof_hook.c \
|
||||
$(srcroot)test/unit/prof_idump.c \
|
||||
$(srcroot)test/unit/prof_log.c \
|
||||
$(srcroot)test/unit/prof_mdump.c \
|
||||
$(srcroot)test/unit/prof_recent.c \
|
||||
$(srcroot)test/unit/prof_reset.c \
|
||||
$(srcroot)test/unit/prof_small.c \
|
||||
$(srcroot)test/unit/prof_stats.c \
|
||||
$(srcroot)test/unit/prof_tctx.c \
|
||||
$(srcroot)test/unit/prof_thread_name.c \
|
||||
$(srcroot)test/unit/prof_sys_thread_name.c \
|
||||
$(srcroot)test/unit/psset.c \
|
||||
$(srcroot)test/unit/ql.c \
|
||||
$(srcroot)test/unit/qr.c \
|
||||
$(srcroot)test/unit/rb.c \
|
||||
$(srcroot)test/unit/retained.c \
|
||||
$(srcroot)test/unit/rtree.c \
|
||||
$(srcroot)test/unit/safety_check.c \
|
||||
$(srcroot)test/unit/sc.c \
|
||||
$(srcroot)test/unit/sec.c \
|
||||
$(srcroot)test/unit/seq.c \
|
||||
$(srcroot)test/unit/SFMT.c \
|
||||
$(srcroot)test/unit/size_check.c \
|
||||
$(srcroot)test/unit/size_classes.c \
|
||||
$(srcroot)test/unit/slab.c \
|
||||
$(srcroot)test/unit/smoothstep.c \
|
||||
$(srcroot)test/unit/spin.c \
|
||||
$(srcroot)test/unit/stats.c \
|
||||
$(srcroot)test/unit/stats_print.c \
|
||||
$(srcroot)test/unit/sz.c \
|
||||
$(srcroot)test/unit/tcache_init.c \
|
||||
$(srcroot)test/unit/tcache_max.c \
|
||||
$(srcroot)test/unit/test_hooks.c \
|
||||
$(srcroot)test/unit/thread_event.c \
|
||||
$(srcroot)test/unit/ticker.c \
|
||||
$(srcroot)test/unit/nstime.c \
|
||||
$(srcroot)test/unit/tsd.c \
|
||||
$(srcroot)test/unit/uaf.c \
|
||||
$(srcroot)test/unit/witness.c \
|
||||
$(srcroot)test/unit/zero.c
|
||||
$(srcroot)test/unit/zero.c \
|
||||
$(srcroot)test/unit/zero_realloc_abort.c \
|
||||
$(srcroot)test/unit/zero_realloc_free.c \
|
||||
$(srcroot)test/unit/zero_realloc_alloc.c \
|
||||
$(srcroot)test/unit/zero_reallocs.c
|
||||
ifeq (@enable_prof@, 1)
|
||||
TESTS_UNIT += \
|
||||
$(srcroot)test/unit/arena_reset_prof.c
|
||||
$(srcroot)test/unit/arena_reset_prof.c \
|
||||
$(srcroot)test/unit/batch_alloc_prof.c
|
||||
endif
|
||||
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
|
||||
$(srcroot)test/integration/allocated.c \
|
||||
$(srcroot)test/integration/extent.c \
|
||||
$(srcroot)test/integration/malloc.c \
|
||||
$(srcroot)test/integration/mallocx.c \
|
||||
$(srcroot)test/integration/MALLOCX_ARENA.c \
|
||||
$(srcroot)test/integration/overflow.c \
|
||||
$(srcroot)test/integration/posix_memalign.c \
|
||||
$(srcroot)test/integration/rallocx.c \
|
||||
$(srcroot)test/integration/sdallocx.c \
|
||||
$(srcroot)test/integration/slab_sizes.c \
|
||||
$(srcroot)test/integration/thread_arena.c \
|
||||
$(srcroot)test/integration/thread_tcache_enabled.c \
|
||||
$(srcroot)test/integration/xallocx.c
|
||||
ifeq (@enable_experimental_smallocx@, 1)
|
||||
TESTS_INTEGRATION += \
|
||||
$(srcroot)test/integration/smallocx.c
|
||||
endif
|
||||
ifeq (@enable_cxx@, 1)
|
||||
CPP_SRCS := $(srcroot)src/jemalloc_cpp.cpp
|
||||
TESTS_INTEGRATION_CPP := $(srcroot)test/integration/cpp/basic.cpp
|
||||
TESTS_INTEGRATION_CPP := $(srcroot)test/integration/cpp/basic.cpp \
|
||||
$(srcroot)test/integration/cpp/infallible_new_true.cpp \
|
||||
$(srcroot)test/integration/cpp/infallible_new_false.cpp
|
||||
else
|
||||
CPP_SRCS :=
|
||||
TESTS_INTEGRATION_CPP :=
|
||||
endif
|
||||
TESTS_STRESS := $(srcroot)test/stress/microbench.c
|
||||
TESTS_ANALYZE := $(srcroot)test/analyze/prof_bias.c \
|
||||
$(srcroot)test/analyze/rand.c \
|
||||
$(srcroot)test/analyze/sizes.c
|
||||
TESTS_STRESS := $(srcroot)test/stress/batch_alloc.c \
|
||||
$(srcroot)test/stress/fill_flush.c \
|
||||
$(srcroot)test/stress/hookbench.c \
|
||||
$(srcroot)test/stress/large_microbench.c \
|
||||
$(srcroot)test/stress/mallctl.c \
|
||||
$(srcroot)test/stress/microbench.c
|
||||
ifeq (@enable_cxx@, 1)
|
||||
TESTS_STRESS_CPP := $(srcroot)test/stress/cpp/microbench.cpp
|
||||
else
|
||||
TESTS_STRESS_CPP :=
|
||||
endif
|
||||
|
||||
TESTS := $(TESTS_UNIT) $(TESTS_INTEGRATION) $(TESTS_INTEGRATION_CPP) $(TESTS_STRESS)
|
||||
|
||||
TESTS := $(TESTS_UNIT) $(TESTS_INTEGRATION) $(TESTS_INTEGRATION_CPP) \
|
||||
$(TESTS_ANALYZE) $(TESTS_STRESS) $(TESTS_STRESS_CPP)
|
||||
|
||||
PRIVATE_NAMESPACE_HDRS := $(objroot)include/jemalloc/internal/private_namespace.h $(objroot)include/jemalloc/internal/private_namespace_jet.h
|
||||
PRIVATE_NAMESPACE_GEN_HDRS := $(PRIVATE_NAMESPACE_HDRS:%.h=%.gen.h)
|
||||
|
|
@ -251,15 +377,21 @@ C_JET_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.$(O))
|
|||
C_TESTLIB_UNIT_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.unit.$(O))
|
||||
C_TESTLIB_INTEGRATION_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))
|
||||
C_UTIL_INTEGRATION_OBJS := $(C_UTIL_INTEGRATION_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))
|
||||
C_TESTLIB_ANALYZE_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.analyze.$(O))
|
||||
C_TESTLIB_STRESS_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.stress.$(O))
|
||||
C_TESTLIB_OBJS := $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(C_TESTLIB_STRESS_OBJS)
|
||||
C_TESTLIB_OBJS := $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) \
|
||||
$(C_UTIL_INTEGRATION_OBJS) $(C_TESTLIB_ANALYZE_OBJS) \
|
||||
$(C_TESTLIB_STRESS_OBJS)
|
||||
|
||||
TESTS_UNIT_OBJS := $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%.$(O))
|
||||
TESTS_INTEGRATION_OBJS := $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%.$(O))
|
||||
TESTS_INTEGRATION_CPP_OBJS := $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%.$(O))
|
||||
TESTS_ANALYZE_OBJS := $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%.$(O))
|
||||
TESTS_STRESS_OBJS := $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%.$(O))
|
||||
TESTS_OBJS := $(TESTS_UNIT_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_STRESS_OBJS)
|
||||
TESTS_CPP_OBJS := $(TESTS_INTEGRATION_CPP_OBJS)
|
||||
TESTS_STRESS_CPP_OBJS := $(TESTS_STRESS_CPP:$(srcroot)%.cpp=$(objroot)%.$(O))
|
||||
TESTS_OBJS := $(TESTS_UNIT_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_ANALYZE_OBJS) \
|
||||
$(TESTS_STRESS_OBJS)
|
||||
TESTS_CPP_OBJS := $(TESTS_INTEGRATION_CPP_OBJS) $(TESTS_STRESS_CPP_OBJS)
|
||||
|
||||
.PHONY: all dist build_doc_html build_doc_man build_doc
|
||||
.PHONY: install_bin install_include install_lib
|
||||
|
|
@ -273,11 +405,32 @@ all: build_lib
|
|||
|
||||
dist: build_doc
|
||||
|
||||
$(objroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
|
||||
$(objroot)doc/%$(install_suffix).html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
|
||||
ifneq ($(XSLROOT),)
|
||||
$(XSLTPROC) -o $@ $(objroot)doc/html.xsl $<
|
||||
else
|
||||
ifeq ($(wildcard $(DOCS_HTML)),)
|
||||
@echo "<p>Missing xsltproc. Doc not built.</p>" > $@
|
||||
endif
|
||||
@echo "Missing xsltproc. "$@" not (re)built."
|
||||
endif
|
||||
|
||||
$(objroot)doc/%.3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
|
||||
$(objroot)doc/%$(install_suffix).3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
|
||||
ifneq ($(XSLROOT),)
|
||||
$(XSLTPROC) -o $@ $(objroot)doc/manpages.xsl $<
|
||||
# The -o option (output filename) of xsltproc may not work (it uses the
|
||||
# <refname> in the .xml file). Manually add the suffix if so.
|
||||
ifneq ($(install_suffix),)
|
||||
@if [ -f $(objroot)doc/jemalloc.3 ]; then \
|
||||
mv $(objroot)doc/jemalloc.3 $(objroot)doc/jemalloc$(install_suffix).3 ; \
|
||||
fi
|
||||
endif
|
||||
else
|
||||
ifeq ($(wildcard $(DOCS_MAN3)),)
|
||||
@echo "Missing xsltproc. Doc not built." > $@
|
||||
endif
|
||||
@echo "Missing xsltproc. "$@" not (re)built."
|
||||
endif
|
||||
|
||||
build_doc_html: $(DOCS_HTML)
|
||||
build_doc_man: $(DOCS_MAN3)
|
||||
|
|
@ -318,17 +471,23 @@ $(C_TESTLIB_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST
|
|||
$(C_TESTLIB_INTEGRATION_OBJS): $(objroot)test/src/%.integration.$(O): $(srcroot)test/src/%.c
|
||||
$(C_TESTLIB_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST
|
||||
$(C_UTIL_INTEGRATION_OBJS): $(objroot)src/%.integration.$(O): $(srcroot)src/%.c
|
||||
$(C_TESTLIB_ANALYZE_OBJS): $(objroot)test/src/%.analyze.$(O): $(srcroot)test/src/%.c
|
||||
$(C_TESTLIB_ANALYZE_OBJS): CPPFLAGS += -DJEMALLOC_ANALYZE_TEST
|
||||
$(C_TESTLIB_STRESS_OBJS): $(objroot)test/src/%.stress.$(O): $(srcroot)test/src/%.c
|
||||
$(C_TESTLIB_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST -DJEMALLOC_STRESS_TESTLIB
|
||||
$(C_TESTLIB_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
|
||||
$(TESTS_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST
|
||||
$(TESTS_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST
|
||||
$(TESTS_INTEGRATION_CPP_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_CPP_TEST
|
||||
$(TESTS_ANALYZE_OBJS): CPPFLAGS += -DJEMALLOC_ANALYZE_TEST
|
||||
$(TESTS_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST
|
||||
$(TESTS_STRESS_CPP_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_CPP_TEST
|
||||
$(TESTS_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.c
|
||||
$(TESTS_CPP_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.cpp
|
||||
$(TESTS_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
|
||||
$(TESTS_CPP_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
|
||||
$(TESTS_OBJS): CFLAGS += -fno-builtin
|
||||
$(TESTS_CPP_OBJS): CPPFLAGS += -fno-builtin
|
||||
ifneq ($(IMPORTLIB),$(SO))
|
||||
$(CPP_OBJS) $(C_SYM_OBJS) $(C_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS): CPPFLAGS += -DDLLEXPORT
|
||||
endif
|
||||
|
|
@ -343,7 +502,7 @@ $(TESTS_OBJS) $(TESTS_CPP_OBJS): $(objroot)test/include/test/jemalloc_test.h
|
|||
endif
|
||||
|
||||
$(C_OBJS) $(CPP_OBJS) $(C_PIC_OBJS) $(CPP_PIC_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_INTEGRATION_CPP_OBJS): $(objroot)include/jemalloc/internal/private_namespace.h
|
||||
$(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_STRESS_OBJS) $(TESTS_UNIT_OBJS) $(TESTS_STRESS_OBJS): $(objroot)include/jemalloc/internal/private_namespace_jet.h
|
||||
$(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_ANALYZE_OBJS) $(C_TESTLIB_STRESS_OBJS) $(TESTS_UNIT_OBJS) $(TESTS_ANALYZE_OBJS) $(TESTS_STRESS_OBJS) $(TESTS_STRESS_CPP_OBJS): $(objroot)include/jemalloc/internal/private_namespace_jet.h
|
||||
|
||||
$(C_SYM_OBJS) $(C_OBJS) $(C_PIC_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS) $(C_TESTLIB_OBJS) $(TESTS_OBJS): %.$(O):
|
||||
@mkdir -p $(@D)
|
||||
|
|
@ -367,7 +526,7 @@ $(objroot)include/jemalloc/internal/private_namespace_jet.gen.h: $(C_JET_SYMS)
|
|||
$(SHELL) $(srcroot)include/jemalloc/internal/private_namespace.sh $^ > $@
|
||||
|
||||
%.h: %.gen.h
|
||||
@if ! `cmp -s $< $@` ; then echo "cp $< $<"; cp $< $@ ; fi
|
||||
@if ! `cmp -s $< $@` ; then echo "cp $< $@"; cp $< $@ ; fi
|
||||
|
||||
$(CPP_OBJS) $(CPP_PIC_OBJS) $(TESTS_CPP_OBJS): %.$(O):
|
||||
@mkdir -p $(@D)
|
||||
|
|
@ -384,7 +543,11 @@ endif
|
|||
|
||||
$(objroot)lib/$(LIBJEMALLOC).$(SOREV) : $(if $(PIC_CFLAGS),$(C_PIC_OBJS),$(C_OBJS)) $(if $(PIC_CFLAGS),$(CPP_PIC_OBJS),$(CPP_OBJS))
|
||||
@mkdir -p $(@D)
|
||||
ifeq (@enable_cxx@, 1)
|
||||
$(CXX) $(DSO_LDFLAGS) $(call RPATH,$(RPATH_EXTRA)) $(LDTARGET) $+ $(LDFLAGS) $(LIBS) $(EXTRA_LDFLAGS)
|
||||
else
|
||||
$(CC) $(DSO_LDFLAGS) $(call RPATH,$(RPATH_EXTRA)) $(LDTARGET) $+ $(LDFLAGS) $(LIBS) $(EXTRA_LDFLAGS)
|
||||
endif
|
||||
|
||||
$(objroot)lib/$(LIBJEMALLOC)_pic.$(A) : $(C_PIC_OBJS) $(CPP_PIC_OBJS)
|
||||
$(objroot)lib/$(LIBJEMALLOC).$(A) : $(C_OBJS) $(CPP_OBJS)
|
||||
|
|
@ -400,19 +563,50 @@ $(objroot)test/unit/%$(EXE): $(objroot)test/unit/%.$(O) $(C_JET_OBJS) $(C_TESTLI
|
|||
|
||||
$(objroot)test/integration/%$(EXE): $(objroot)test/integration/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LJEMALLOC) $(LDFLAGS) $(filter-out -lm,$(filter -lrt -lpthread -lstdc++,$(LIBS))) $(LM) $(EXTRA_LDFLAGS)
|
||||
$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LJEMALLOC) $(LDFLAGS) $(filter-out -lm,$(filter -lrt -pthread -lstdc++,$(LIBS))) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/integration/cpp/%$(EXE): $(objroot)test/integration/cpp/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/analyze/%$(EXE): $(objroot)test/analyze/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_ANALYZE_OBJS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/stress/%$(EXE): $(objroot)test/stress/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_STRESS_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/stress/pa/pa_data_preprocessor$(EXE): $(objroot)test/stress/pa/pa_data_preprocessor.$(O)
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(LDTARGET) $(filter %.$(O),$^) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/stress/pa/pa_microbench$(EXE): $(objroot)test/stress/pa/pa_microbench.$(O) $(C_JET_OBJS) $(C_TESTLIB_STRESS_OBJS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
|
||||
|
||||
$(objroot)test/stress/pa/%.$(O): $(srcroot)test/stress/pa/%.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -c $(CPPFLAGS) -DJEMALLOC_STRESS_TEST -I$(srcroot)test/include -I$(objroot)test/include $(CTARGET) $<
|
||||
ifdef CC_MM
|
||||
@$(CC) -MM $(CPPFLAGS) -DJEMALLOC_STRESS_TEST -I$(srcroot)test/include -I$(objroot)test/include -MT $@ -o $(@:%.$(O)=%.d) $<
|
||||
endif
|
||||
|
||||
$(objroot)test/stress/pa/%.$(O): $(srcroot)test/stress/pa/%.cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) $(CXXFLAGS) -c $(CPPFLAGS) -I$(srcroot)test/include -I$(objroot)test/include $(CTARGET) $<
|
||||
ifdef CC_MM
|
||||
@$(CXX) -MM $(CPPFLAGS) -I$(srcroot)test/include -I$(objroot)test/include -MT $@ -o $(@:%.$(O)=%.d) $<
|
||||
endif
|
||||
|
||||
build_lib_shared: $(DSOS)
|
||||
build_lib_static: $(STATIC_LIBS)
|
||||
build_lib: build_lib_shared build_lib_static
|
||||
ifeq ($(enable_shared), 1)
|
||||
build_lib: build_lib_shared
|
||||
endif
|
||||
ifeq ($(enable_static), 1)
|
||||
build_lib: build_lib_static
|
||||
endif
|
||||
|
||||
install_bin:
|
||||
$(INSTALL) -d $(BINDIR)
|
||||
|
|
@ -449,16 +643,22 @@ install_lib_pc: $(PC)
|
|||
$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig; \
|
||||
done
|
||||
|
||||
install_lib: install_lib_shared install_lib_static install_lib_pc
|
||||
ifeq ($(enable_shared), 1)
|
||||
install_lib: install_lib_shared
|
||||
endif
|
||||
ifeq ($(enable_static), 1)
|
||||
install_lib: install_lib_static
|
||||
endif
|
||||
install_lib: install_lib_pc
|
||||
|
||||
install_doc_html:
|
||||
install_doc_html: build_doc_html
|
||||
$(INSTALL) -d $(DATADIR)/doc/jemalloc$(install_suffix)
|
||||
@for d in $(DOCS_HTML); do \
|
||||
echo "$(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix)"; \
|
||||
$(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \
|
||||
done
|
||||
|
||||
install_doc_man:
|
||||
install_doc_man: build_doc_man
|
||||
$(INSTALL) -d $(MANDIR)/man3
|
||||
@for d in $(DOCS_MAN3); do \
|
||||
echo "$(INSTALL) -m 644 $$d $(MANDIR)/man3"; \
|
||||
|
|
@ -467,17 +667,67 @@ done
|
|||
|
||||
install_doc: install_doc_html install_doc_man
|
||||
|
||||
install: install_bin install_include install_lib install_doc
|
||||
install: install_bin install_include install_lib
|
||||
|
||||
ifeq ($(enable_doc), 1)
|
||||
install: install_doc
|
||||
endif
|
||||
|
||||
uninstall_bin:
|
||||
$(RM) -v $(foreach b,$(notdir $(BINS)),$(BINDIR)/$(b))
|
||||
|
||||
uninstall_include:
|
||||
$(RM) -v $(foreach h,$(notdir $(C_HDRS)),$(INCLUDEDIR)/jemalloc/$(h))
|
||||
rmdir -v $(INCLUDEDIR)/jemalloc
|
||||
|
||||
uninstall_lib_shared:
|
||||
$(RM) -v $(LIBDIR)/$(LIBJEMALLOC).$(SOREV)
|
||||
ifneq ($(SOREV),$(SO))
|
||||
$(RM) -v $(LIBDIR)/$(LIBJEMALLOC).$(SO)
|
||||
endif
|
||||
|
||||
uninstall_lib_static:
|
||||
$(RM) -v $(foreach l,$(notdir $(STATIC_LIBS)),$(LIBDIR)/$(l))
|
||||
|
||||
uninstall_lib_pc:
|
||||
$(RM) -v $(foreach p,$(notdir $(PC)),$(LIBDIR)/pkgconfig/$(p))
|
||||
|
||||
ifeq ($(enable_shared), 1)
|
||||
uninstall_lib: uninstall_lib_shared
|
||||
endif
|
||||
ifeq ($(enable_static), 1)
|
||||
uninstall_lib: uninstall_lib_static
|
||||
endif
|
||||
uninstall_lib: uninstall_lib_pc
|
||||
|
||||
uninstall_doc_html:
|
||||
$(RM) -v $(foreach d,$(notdir $(DOCS_HTML)),$(DATADIR)/doc/jemalloc$(install_suffix)/$(d))
|
||||
rmdir -v $(DATADIR)/doc/jemalloc$(install_suffix)
|
||||
|
||||
uninstall_doc_man:
|
||||
$(RM) -v $(foreach d,$(notdir $(DOCS_MAN3)),$(MANDIR)/man3/$(d))
|
||||
|
||||
uninstall_doc: uninstall_doc_html uninstall_doc_man
|
||||
|
||||
uninstall: uninstall_bin uninstall_include uninstall_lib
|
||||
|
||||
ifeq ($(enable_doc), 1)
|
||||
uninstall: uninstall_doc
|
||||
endif
|
||||
|
||||
tests_unit: $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%$(EXE))
|
||||
tests_integration: $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%$(EXE)) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%$(EXE))
|
||||
tests_stress: $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%$(EXE))
|
||||
tests: tests_unit tests_integration tests_stress
|
||||
tests_analyze: $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%$(EXE))
|
||||
tests_stress: $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%$(EXE)) $(TESTS_STRESS_CPP:$(srcroot)%.cpp=$(objroot)%$(EXE))
|
||||
tests_pa: $(objroot)test/stress/pa/pa_data_preprocessor$(EXE) $(objroot)test/stress/pa/pa_microbench$(EXE)
|
||||
tests: tests_unit tests_integration tests_analyze tests_stress
|
||||
|
||||
check_unit_dir:
|
||||
@mkdir -p $(objroot)test/unit
|
||||
check_integration_dir:
|
||||
@mkdir -p $(objroot)test/integration
|
||||
analyze_dir:
|
||||
@mkdir -p $(objroot)test/analyze
|
||||
stress_dir:
|
||||
@mkdir -p $(objroot)test/stress
|
||||
check_dir: check_unit_dir check_integration_dir
|
||||
|
|
@ -494,8 +744,15 @@ check_integration_decay: tests_integration check_integration_dir
|
|||
$(MALLOC_CONF)="dirty_decay_ms:0,muzzy_decay_ms:0" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)
|
||||
check_integration: tests_integration check_integration_dir
|
||||
$(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)
|
||||
analyze: tests_analyze analyze_dir
|
||||
ifeq ($(enable_prof), 1)
|
||||
$(MALLOC_CONF)="prof:true" $(SHELL) $(objroot)test/test.sh $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%)
|
||||
else
|
||||
$(SHELL) $(objroot)test/test.sh $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%)
|
||||
endif
|
||||
stress: tests_stress stress_dir
|
||||
$(SHELL) $(objroot)test/test.sh $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%)
|
||||
$(SHELL) $(objroot)test/test.sh $(TESTS_STRESS_CPP:$(srcroot)%.cpp=$(objroot)%)
|
||||
check: check_unit check_integration check_integration_decay check_integration_prof
|
||||
|
||||
clean:
|
||||
|
|
|
|||
2
README
2
README
|
|
@ -17,4 +17,4 @@ jemalloc.
|
|||
|
||||
The ChangeLog file contains a brief summary of changes for each release.
|
||||
|
||||
URL: http://jemalloc.net/
|
||||
URL: https://jemalloc.net/
|
||||
|
|
|
|||
34
TUNING.md
34
TUNING.md
|
|
@ -1,5 +1,5 @@
|
|||
This document summarizes the common approaches for performance fine tuning with
|
||||
jemalloc (as of 5.1.0). The default configuration of jemalloc tends to work
|
||||
jemalloc (as of 5.3.0). The default configuration of jemalloc tends to work
|
||||
reasonably well in practice, and most applications should not have to tune any
|
||||
options. However, in order to cover a wide range of applications and avoid
|
||||
pathological cases, the default setting is sometimes kept conservative and
|
||||
|
|
@ -11,9 +11,9 @@ by a few percent, or make favorable trade-offs.
|
|||
## Notable runtime options for performance tuning
|
||||
|
||||
Runtime options can be set via
|
||||
[malloc_conf](http://jemalloc.net/jemalloc.3.html#tuning).
|
||||
[malloc_conf](https://jemalloc.net/jemalloc.3.html#tuning).
|
||||
|
||||
* [background_thread](http://jemalloc.net/jemalloc.3.html#background_thread)
|
||||
* [background_thread](https://jemalloc.net/jemalloc.3.html#background_thread)
|
||||
|
||||
Enabling jemalloc background threads generally improves the tail latency for
|
||||
application threads, since unused memory purging is shifted to the dedicated
|
||||
|
|
@ -23,7 +23,7 @@ Runtime options can be set via
|
|||
Suggested: `background_thread:true` when jemalloc managed threads can be
|
||||
allowed.
|
||||
|
||||
* [metadata_thp](http://jemalloc.net/jemalloc.3.html#opt.metadata_thp)
|
||||
* [metadata_thp](https://jemalloc.net/jemalloc.3.html#opt.metadata_thp)
|
||||
|
||||
Allowing jemalloc to utilize transparent huge pages for its internal
|
||||
metadata usually reduces TLB misses significantly, especially for programs
|
||||
|
|
@ -35,8 +35,8 @@ Runtime options can be set via
|
|||
`metadata_thp:always`, which is expected to improve CPU utilization at a
|
||||
small memory cost.
|
||||
|
||||
* [dirty_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.dirty_decay_ms) and
|
||||
[muzzy_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.muzzy_decay_ms)
|
||||
* [dirty_decay_ms](https://jemalloc.net/jemalloc.3.html#opt.dirty_decay_ms) and
|
||||
[muzzy_decay_ms](https://jemalloc.net/jemalloc.3.html#opt.muzzy_decay_ms)
|
||||
|
||||
Decay time determines how fast jemalloc returns unused pages back to the
|
||||
operating system, and therefore provides a fairly straightforward trade-off
|
||||
|
|
@ -46,7 +46,7 @@ Runtime options can be set via
|
|||
|
||||
Suggested: tune the values based on the desired trade-offs.
|
||||
|
||||
* [narenas](http://jemalloc.net/jemalloc.3.html#opt.narenas)
|
||||
* [narenas](https://jemalloc.net/jemalloc.3.html#opt.narenas)
|
||||
|
||||
By default jemalloc uses multiple arenas to reduce internal lock contention.
|
||||
However high arena count may also increase overall memory fragmentation,
|
||||
|
|
@ -57,7 +57,7 @@ Runtime options can be set via
|
|||
Suggested: if low parallelism is expected, try lower arena count while
|
||||
monitoring CPU and memory usage.
|
||||
|
||||
* [percpu_arena](http://jemalloc.net/jemalloc.3.html#opt.percpu_arena)
|
||||
* [percpu_arena](https://jemalloc.net/jemalloc.3.html#opt.percpu_arena)
|
||||
|
||||
Enable dynamic thread to arena association based on running CPU. This has
|
||||
the potential to improve locality, e.g. when thread to CPU affinity is
|
||||
|
|
@ -76,14 +76,14 @@ Examples:
|
|||
|
||||
* High resource consumption application, prioritizing memory usage:
|
||||
|
||||
`background_thread:true` combined with shorter decay time (decreased
|
||||
`dirty_decay_ms` and / or `muzzy_decay_ms`,
|
||||
`background_thread:true,tcache_max:4096` combined with shorter decay time
|
||||
(decreased `dirty_decay_ms` and / or `muzzy_decay_ms`,
|
||||
e.g. `dirty_decay_ms:5000,muzzy_decay_ms:5000`), and lower arena count
|
||||
(e.g. number of CPUs).
|
||||
|
||||
* Low resource consumption application:
|
||||
|
||||
`narenas:1,lg_tcache_max:13` combined with shorter decay time (decreased
|
||||
`narenas:1,tcache_max:1024` combined with shorter decay time (decreased
|
||||
`dirty_decay_ms` and / or `muzzy_decay_ms`,e.g.
|
||||
`dirty_decay_ms:1000,muzzy_decay_ms:0`).
|
||||
|
||||
|
|
@ -100,28 +100,28 @@ aborts immediately on illegal options.
|
|||
In addition to the runtime options, there are a number of programmatic ways to
|
||||
improve application performance with jemalloc.
|
||||
|
||||
* [Explicit arenas](http://jemalloc.net/jemalloc.3.html#arenas.create)
|
||||
* [Explicit arenas](https://jemalloc.net/jemalloc.3.html#arenas.create)
|
||||
|
||||
Manually created arenas can help performance in various ways, e.g. by
|
||||
managing locality and contention for specific usages. For example,
|
||||
applications can explicitly allocate frequently accessed objects from a
|
||||
dedicated arena with
|
||||
[mallocx()](http://jemalloc.net/jemalloc.3.html#MALLOCX_ARENA) to improve
|
||||
[mallocx()](https://jemalloc.net/jemalloc.3.html#MALLOCX_ARENA) to improve
|
||||
locality. In addition, explicit arenas often benefit from individually
|
||||
tuned options, e.g. relaxed [decay
|
||||
time](http://jemalloc.net/jemalloc.3.html#arena.i.dirty_decay_ms) if
|
||||
time](https://jemalloc.net/jemalloc.3.html#arena.i.dirty_decay_ms) if
|
||||
frequent reuse is expected.
|
||||
|
||||
* [Extent hooks](http://jemalloc.net/jemalloc.3.html#arena.i.extent_hooks)
|
||||
* [Extent hooks](https://jemalloc.net/jemalloc.3.html#arena.i.extent_hooks)
|
||||
|
||||
Extent hooks allow customization for managing underlying memory. One use
|
||||
case for performance purpose is to utilize huge pages -- for example,
|
||||
[HHVM](https://github.com/facebook/hhvm/blob/master/hphp/util/alloc.cpp)
|
||||
[HHVM](httpss://github.com/facebook/hhvm/blob/master/hphp/util/alloc.cpp)
|
||||
uses explicit arenas with customized extent hooks to manage 1GB huge pages
|
||||
for frequently accessed data, which reduces TLB misses significantly.
|
||||
|
||||
* [Explicit thread-to-arena
|
||||
binding](http://jemalloc.net/jemalloc.3.html#thread.arena)
|
||||
binding](https://jemalloc.net/jemalloc.3.html#thread.arena)
|
||||
|
||||
It is common for some threads in an application to have different memory
|
||||
access / allocation patterns. Threads with heavy workloads often benefit
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ for i in autoconf; do
|
|||
fi
|
||||
done
|
||||
|
||||
echo "./configure --enable-autogen $@"
|
||||
./configure --enable-autogen $@
|
||||
echo "./configure --enable-autogen \"$@\""
|
||||
./configure --enable-autogen "$@"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error $? in ./configure"
|
||||
exit 1
|
||||
|
|
|
|||
218
bin/jeprof.in
218
bin/jeprof.in
|
|
@ -88,6 +88,7 @@ my %obj_tool_map = (
|
|||
#"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables
|
||||
#"addr2line_pdb" => "addr2line-pdb", # ditto
|
||||
#"otool" => "otool", # equivalent of objdump on OS X
|
||||
#"dyld_info" => "dyld_info", # equivalent of otool on OS X for shared cache
|
||||
);
|
||||
# NOTE: these are lists, so you can put in commandline flags if you want.
|
||||
my @DOT = ("dot"); # leave non-absolute, since it may be in /usr/local
|
||||
|
|
@ -205,6 +206,8 @@ Output type:
|
|||
--svg Generate SVG to stdout
|
||||
--gif Generate GIF to stdout
|
||||
--raw Generate symbolized jeprof data (useful with remote fetch)
|
||||
--collapsed Generate collapsed stacks for building flame graphs
|
||||
(see http://www.brendangregg.com/flamegraphs.html)
|
||||
|
||||
Heap-Profile Options:
|
||||
--inuse_space Display in-use (mega)bytes [default]
|
||||
|
|
@ -238,6 +241,7 @@ Miscellaneous:
|
|||
--test Run unit tests
|
||||
--help This message
|
||||
--version Version information
|
||||
--debug-syms-by-id (Linux only) Find debug symbol files by build ID as well as by name
|
||||
|
||||
Environment Variables:
|
||||
JEPROF_TMPDIR Profiles directory. Defaults to \$HOME/jeprof
|
||||
|
|
@ -332,6 +336,7 @@ sub Init() {
|
|||
$main::opt_gif = 0;
|
||||
$main::opt_svg = 0;
|
||||
$main::opt_raw = 0;
|
||||
$main::opt_collapsed = 0;
|
||||
|
||||
$main::opt_nodecount = 80;
|
||||
$main::opt_nodefraction = 0.005;
|
||||
|
|
@ -362,6 +367,7 @@ sub Init() {
|
|||
$main::opt_tools = "";
|
||||
$main::opt_debug = 0;
|
||||
$main::opt_test = 0;
|
||||
$main::opt_debug_syms_by_id = 0;
|
||||
|
||||
# These are undocumented flags used only by unittests.
|
||||
$main::opt_test_stride = 0;
|
||||
|
|
@ -405,6 +411,7 @@ sub Init() {
|
|||
"svg!" => \$main::opt_svg,
|
||||
"gif!" => \$main::opt_gif,
|
||||
"raw!" => \$main::opt_raw,
|
||||
"collapsed!" => \$main::opt_collapsed,
|
||||
"interactive!" => \$main::opt_interactive,
|
||||
"nodecount=i" => \$main::opt_nodecount,
|
||||
"nodefraction=f" => \$main::opt_nodefraction,
|
||||
|
|
@ -429,6 +436,7 @@ sub Init() {
|
|||
"tools=s" => \$main::opt_tools,
|
||||
"test!" => \$main::opt_test,
|
||||
"debug!" => \$main::opt_debug,
|
||||
"debug-syms-by-id!" => \$main::opt_debug_syms_by_id,
|
||||
# Undocumented flags used only by unittests:
|
||||
"test_stride=i" => \$main::opt_test_stride,
|
||||
) || usage("Invalid option(s)");
|
||||
|
|
@ -490,6 +498,7 @@ sub Init() {
|
|||
$main::opt_svg +
|
||||
$main::opt_gif +
|
||||
$main::opt_raw +
|
||||
$main::opt_collapsed +
|
||||
$main::opt_interactive +
|
||||
0;
|
||||
if ($modes > 1) {
|
||||
|
|
@ -572,6 +581,11 @@ sub Init() {
|
|||
foreach (@prefix_list) {
|
||||
s|/+$||;
|
||||
}
|
||||
|
||||
# Flag to prevent us from trying over and over to use
|
||||
# elfutils if it's not installed (used only with
|
||||
# --debug-syms-by-id option).
|
||||
$main::gave_up_on_elfutils = 0;
|
||||
}
|
||||
|
||||
sub FilterAndPrint {
|
||||
|
|
@ -621,6 +635,8 @@ sub FilterAndPrint {
|
|||
PrintText($symbols, $flat, $cumulative, -1);
|
||||
} elsif ($main::opt_raw) {
|
||||
PrintSymbolizedProfile($symbols, $profile, $main::prog);
|
||||
} elsif ($main::opt_collapsed) {
|
||||
PrintCollapsedStacks($symbols, $profile);
|
||||
} elsif ($main::opt_callgrind) {
|
||||
PrintCallgrind($calls);
|
||||
} else {
|
||||
|
|
@ -673,15 +689,15 @@ sub Main() {
|
|||
my $symbol_map = {};
|
||||
|
||||
# Read one profile, pick the last item on the list
|
||||
my $data = ReadProfile($main::prog, pop(@main::profile_files));
|
||||
my $data = ReadProfile($main::prog, $main::profile_files[0]);
|
||||
my $profile = $data->{profile};
|
||||
my $pcs = $data->{pcs};
|
||||
my $libs = $data->{libs}; # Info about main program and shared libraries
|
||||
$symbol_map = MergeSymbols($symbol_map, $data->{symbols});
|
||||
|
||||
# Add additional profiles, if available.
|
||||
if (scalar(@main::profile_files) > 0) {
|
||||
foreach my $pname (@main::profile_files) {
|
||||
if (scalar(@main::profile_files) > 1) {
|
||||
foreach my $pname (@main::profile_files[1..$#main::profile_files]) {
|
||||
my $data2 = ReadProfile($main::prog, $pname);
|
||||
$profile = AddProfile($profile, $data2->{profile});
|
||||
$pcs = AddPcs($pcs, $data2->{pcs});
|
||||
|
|
@ -2810,6 +2826,40 @@ sub IsSecondPcAlwaysTheSame {
|
|||
return $second_pc;
|
||||
}
|
||||
|
||||
sub ExtractSymbolNameInlineStack {
|
||||
my $symbols = shift;
|
||||
my $address = shift;
|
||||
|
||||
my @stack = ();
|
||||
|
||||
if (exists $symbols->{$address}) {
|
||||
my @localinlinestack = @{$symbols->{$address}};
|
||||
for (my $i = $#localinlinestack; $i > 0; $i-=3) {
|
||||
my $file = $localinlinestack[$i-1];
|
||||
my $fn = $localinlinestack[$i-0];
|
||||
|
||||
if ($file eq "?" || $file eq ":0") {
|
||||
$file = "??:0";
|
||||
}
|
||||
if ($fn eq '??') {
|
||||
# If we can't get the symbol name, at least use the file information.
|
||||
$fn = $file;
|
||||
}
|
||||
my $suffix = "[inline]";
|
||||
if ($i == 2) {
|
||||
$suffix = "";
|
||||
}
|
||||
push (@stack, $fn.$suffix);
|
||||
}
|
||||
}
|
||||
else {
|
||||
# If we can't get a symbol name, at least fill in the address.
|
||||
push (@stack, $address);
|
||||
}
|
||||
|
||||
return @stack;
|
||||
}
|
||||
|
||||
sub ExtractSymbolLocation {
|
||||
my $symbols = shift;
|
||||
my $address = shift;
|
||||
|
|
@ -2884,6 +2934,17 @@ sub FilterFrames {
|
|||
return $result;
|
||||
}
|
||||
|
||||
sub PrintCollapsedStacks {
|
||||
my $symbols = shift;
|
||||
my $profile = shift;
|
||||
|
||||
while (my ($stack_trace, $count) = each %$profile) {
|
||||
my @address = split(/\n/, $stack_trace);
|
||||
my @names = reverse ( map { ExtractSymbolNameInlineStack($symbols, $_) } @address );
|
||||
printf("%s %d\n", join(";", @names), $count);
|
||||
}
|
||||
}
|
||||
|
||||
sub RemoveUninterestingFrames {
|
||||
my $symbols = shift;
|
||||
my $profile = shift;
|
||||
|
|
@ -2895,8 +2956,25 @@ sub RemoveUninterestingFrames {
|
|||
foreach my $name ('@JEMALLOC_PREFIX@calloc',
|
||||
'cfree',
|
||||
'@JEMALLOC_PREFIX@malloc',
|
||||
'je_malloc_default',
|
||||
'newImpl',
|
||||
'void* newImpl',
|
||||
'fallbackNewImpl',
|
||||
'void* fallbackNewImpl',
|
||||
'fallback_impl',
|
||||
'void* fallback_impl',
|
||||
'imalloc',
|
||||
'int imalloc',
|
||||
'imalloc_body',
|
||||
'int imalloc_body',
|
||||
'prof_alloc_prep',
|
||||
'prof_tctx_t *prof_alloc_prep',
|
||||
'prof_backtrace_impl',
|
||||
'void prof_backtrace_impl',
|
||||
'je_prof_backtrace',
|
||||
'void je_prof_backtrace',
|
||||
'je_prof_tctx_create',
|
||||
'prof_tctx_t* prof_tctx_create',
|
||||
'@JEMALLOC_PREFIX@free',
|
||||
'@JEMALLOC_PREFIX@memalign',
|
||||
'@JEMALLOC_PREFIX@posix_memalign',
|
||||
|
|
@ -2905,10 +2983,16 @@ sub RemoveUninterestingFrames {
|
|||
'@JEMALLOC_PREFIX@valloc',
|
||||
'@JEMALLOC_PREFIX@realloc',
|
||||
'@JEMALLOC_PREFIX@mallocx',
|
||||
'irallocx_prof',
|
||||
'void *irallocx_prof',
|
||||
'@JEMALLOC_PREFIX@rallocx',
|
||||
'do_rallocx',
|
||||
'ixallocx_prof',
|
||||
'size_t ixallocx_prof',
|
||||
'@JEMALLOC_PREFIX@xallocx',
|
||||
'@JEMALLOC_PREFIX@dallocx',
|
||||
'@JEMALLOC_PREFIX@sdallocx',
|
||||
'@JEMALLOC_PREFIX@sdallocx_noflags',
|
||||
'tc_calloc',
|
||||
'tc_cfree',
|
||||
'tc_malloc',
|
||||
|
|
@ -3017,6 +3101,8 @@ sub RemoveUninterestingFrames {
|
|||
foreach my $a (@addrs) {
|
||||
if (exists($symbols->{$a})) {
|
||||
my $func = $symbols->{$a}->[0];
|
||||
# Remove suffix in the symbols following space when filtering.
|
||||
$func =~ s/ .*//;
|
||||
if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
|
||||
# Throw away the portion of the backtrace seen so far, under the
|
||||
# assumption that previous frames were for functions internal to the
|
||||
|
|
@ -4439,16 +4525,54 @@ sub FindLibrary {
|
|||
# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
|
||||
sub DebuggingLibrary {
|
||||
my $file = shift;
|
||||
if ($file =~ m|^/|) {
|
||||
if (-f "/usr/lib/debug$file") {
|
||||
return "/usr/lib/debug$file";
|
||||
} elsif (-f "/usr/lib/debug$file.debug") {
|
||||
return "/usr/lib/debug$file.debug";
|
||||
}
|
||||
|
||||
if ($file !~ m|^/|) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Find debug symbol file if it's named after the library's name.
|
||||
|
||||
if (-f "/usr/lib/debug$file") {
|
||||
if($main::opt_debug) { print STDERR "found debug info for $file in /usr/lib/debug$file\n"; }
|
||||
return "/usr/lib/debug$file";
|
||||
} elsif (-f "/usr/lib/debug$file.debug") {
|
||||
if($main::opt_debug) { print STDERR "found debug info for $file in /usr/lib/debug$file.debug\n"; }
|
||||
return "/usr/lib/debug$file.debug";
|
||||
}
|
||||
|
||||
if(!$main::opt_debug_syms_by_id) {
|
||||
if($main::opt_debug) { print STDERR "no debug symbols found for $file\n" };
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Find debug file if it's named after the library's build ID.
|
||||
|
||||
my $readelf = '';
|
||||
if (!$main::gave_up_on_elfutils) {
|
||||
$readelf = qx/eu-readelf -n ${file}/;
|
||||
if ($?) {
|
||||
print STDERR "Cannot run eu-readelf. To use --debug-syms-by-id you must be on Linux, with elfutils installed.\n";
|
||||
$main::gave_up_on_elfutils = 1;
|
||||
return undef;
|
||||
}
|
||||
my $buildID = $1 if $readelf =~ /Build ID: ([A-Fa-f0-9]+)/s;
|
||||
if (defined $buildID && length $buildID > 0) {
|
||||
my $symbolFile = '/usr/lib/debug/.build-id/' . substr($buildID, 0, 2) . '/' . substr($buildID, 2) . '.debug';
|
||||
if (-e $symbolFile) {
|
||||
if($main::opt_debug) { print STDERR "found debug symbol file $symbolFile for $file\n" };
|
||||
return $symbolFile;
|
||||
} else {
|
||||
if($main::opt_debug) { print STDERR "no debug symbol file found for $file, build ID: $buildID\n" };
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($main::opt_debug) { print STDERR "no debug symbols found for $file, build ID unknown\n" };
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
# Parse text section header of a library using objdump
|
||||
sub ParseTextSectionHeaderFromObjdump {
|
||||
my $lib = shift;
|
||||
|
|
@ -4558,7 +4682,65 @@ sub ParseTextSectionHeaderFromOtool {
|
|||
return $r;
|
||||
}
|
||||
|
||||
# Parse text section header of a library in OS X shared cache using dyld_info
|
||||
sub ParseTextSectionHeaderFromDyldInfo {
|
||||
my $lib = shift;
|
||||
|
||||
my $size = undef;
|
||||
my $vma;
|
||||
my $file_offset;
|
||||
# Get dyld_info output from the library file to figure out how to
|
||||
# map between mapped addresses and addresses in the library.
|
||||
my $cmd = ShellEscape($obj_tool_map{"dyld_info"}, "-segments", $lib);
|
||||
open(DYLD, "$cmd |") || error("$cmd: $!\n");
|
||||
|
||||
while (<DYLD>) {
|
||||
s/\r//g; # turn windows-looking lines into unix-looking lines
|
||||
# -segments:
|
||||
# load-address segment section sect-size seg-size perm
|
||||
# 0x1803E0000 __TEXT 112KB r.x
|
||||
# 0x1803E4F34 __text 80960
|
||||
# 0x1803F8B74 __auth_stubs 768
|
||||
# 0x1803F8E74 __init_offsets 4
|
||||
# 0x1803F8E78 __gcc_except_tab 1180
|
||||
my @x = split;
|
||||
if ($#x >= 2) {
|
||||
if ($x[0] eq 'load-offset') {
|
||||
# dyld_info should only be used for the shared lib.
|
||||
return undef;
|
||||
} elsif ($x[1] eq '__TEXT') {
|
||||
$file_offset = $x[0];
|
||||
} elsif ($x[1] eq '__text') {
|
||||
$size = $x[2];
|
||||
$vma = $x[0];
|
||||
$file_offset = AddressSub($x[0], $file_offset);
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(DYLD);
|
||||
|
||||
if (!defined($vma) || !defined($size) || !defined($file_offset)) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $r = {};
|
||||
$r->{size} = $size;
|
||||
$r->{vma} = $vma;
|
||||
$r->{file_offset} = $file_offset;
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
sub ParseTextSectionHeader {
|
||||
# obj_tool_map("dyld_info") is only defined if we're in a Mach-O environment
|
||||
if (defined($obj_tool_map{"dyld_info"})) {
|
||||
my $r = ParseTextSectionHeaderFromDyldInfo(@_);
|
||||
if (defined($r)){
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
# if dyld_info doesn't work, or we don't have it, fall back to otool
|
||||
# obj_tool_map("otool") is only defined if we're in a Mach-O environment
|
||||
if (defined($obj_tool_map{"otool"})) {
|
||||
my $r = ParseTextSectionHeaderFromOtool(@_);
|
||||
|
|
@ -4599,7 +4781,7 @@ sub ParseLibraries {
|
|||
$offset = HexExtend($3);
|
||||
$lib = $4;
|
||||
$lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
|
||||
} elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
|
||||
} elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.(so|dll|dylib|bundle)(\.\d+)*)/) {
|
||||
# Cooked line from DumpAddressMap. Example:
|
||||
# 40000000-40015000: /lib/ld-2.3.2.so
|
||||
$start = HexExtend($1);
|
||||
|
|
@ -4616,6 +4798,15 @@ sub ParseLibraries {
|
|||
$offset = HexExtend($3);
|
||||
$lib = $4;
|
||||
$lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
|
||||
} elsif (($l =~ /^\s*($h)-($h):\s*(\S+)/) && ($3 eq $prog)) {
|
||||
# PIEs and address space randomization do not play well with our
|
||||
# default assumption that main executable is at lowest
|
||||
# addresses. So we're detecting main executable from
|
||||
# DumpAddressMap as well.
|
||||
$start = HexExtend($1);
|
||||
$finish = HexExtend($2);
|
||||
$offset = $zero_offset;
|
||||
$lib = $3;
|
||||
}
|
||||
# FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
|
||||
# function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
|
||||
|
|
@ -4986,7 +5177,7 @@ sub MapToSymbols {
|
|||
} else {
|
||||
# MapSymbolsWithNM tags each routine with its starting address,
|
||||
# useful in case the image has multiple occurrences of this
|
||||
# routine. (It uses a syntax that resembles template paramters,
|
||||
# routine. (It uses a syntax that resembles template parameters,
|
||||
# that are automatically stripped out by ShortFunctionName().)
|
||||
# addr2line does not provide the same information. So we check
|
||||
# if nm disambiguated our symbol, and if so take the annotated
|
||||
|
|
@ -5146,6 +5337,7 @@ sub ConfigureObjTools {
|
|||
if ($file_type =~ /Mach-O/) {
|
||||
# OS X uses otool to examine Mach-O files, rather than objdump.
|
||||
$obj_tool_map{"otool"} = "otool";
|
||||
$obj_tool_map{"dyld_info"} = "dyld_info";
|
||||
$obj_tool_map{"addr2line"} = "false"; # no addr2line
|
||||
$obj_tool_map{"objdump"} = "false"; # no objdump
|
||||
}
|
||||
|
|
@ -5338,7 +5530,7 @@ sub GetProcedureBoundaries {
|
|||
# "nm -f $image" is supposed to fail on GNU nm, but if:
|
||||
#
|
||||
# a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
|
||||
# b. you have a.out in your current directory (a not uncommon occurence)
|
||||
# b. you have a.out in your current directory (a not uncommon occurrence)
|
||||
#
|
||||
# then "nm -f $image" succeeds because -f only looks at the first letter of
|
||||
# the argument, which looks valid because it's [BbSsPp], and then since
|
||||
|
|
@ -5366,7 +5558,7 @@ sub GetProcedureBoundaries {
|
|||
my $demangle_flag = "";
|
||||
my $cppfilt_flag = "";
|
||||
my $to_devnull = ">$dev_null 2>&1";
|
||||
if (system(ShellEscape($nm, "--demangle", "image") . $to_devnull) == 0) {
|
||||
if (system(ShellEscape($nm, "--demangle", $image) . $to_devnull) == 0) {
|
||||
# In this mode, we do "nm --demangle <foo>"
|
||||
$demangle_flag = "--demangle";
|
||||
$cppfilt_flag = "";
|
||||
|
|
|
|||
1706
build-aux/config.guess
vendored
1706
build-aux/config.guess
vendored
File diff suppressed because it is too large
Load diff
3455
build-aux/config.sub
vendored
3455
build-aux/config.sub
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -115,7 +115,7 @@ fi
|
|||
if [ x"$dir_arg" != x ]; then
|
||||
dst=$src
|
||||
src=""
|
||||
|
||||
|
||||
if [ -d $dst ]; then
|
||||
instcmd=:
|
||||
else
|
||||
|
|
@ -124,7 +124,7 @@ if [ x"$dir_arg" != x ]; then
|
|||
else
|
||||
|
||||
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
|
||||
if [ -f $src -o -d $src ]
|
||||
|
|
@ -134,7 +134,7 @@ else
|
|||
echo "install: $src does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
|
|
@ -201,17 +201,17 @@ else
|
|||
|
||||
# If we're going to rename the final executable, determine the name now.
|
||||
|
||||
if [ x"$transformarg" = x ]
|
||||
if [ x"$transformarg" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
dstfile=`basename $dst $transformbasename |
|
||||
dstfile=`basename $dst $transformbasename |
|
||||
sed $transformarg`$transformbasename
|
||||
fi
|
||||
|
||||
# don't allow the sed command to completely eliminate the filename
|
||||
|
||||
if [ x"$dstfile" = x ]
|
||||
if [ x"$dstfile" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
|
|
@ -242,7 +242,7 @@ else
|
|||
# Now rename the file to the real destination.
|
||||
|
||||
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||
|
||||
fi &&
|
||||
|
||||
|
|
|
|||
1427
configure.ac
1427
configure.ac
File diff suppressed because it is too large
Load diff
|
|
@ -33,6 +33,8 @@
|
|||
<refname>aligned_alloc</refname>
|
||||
<refname>realloc</refname>
|
||||
<refname>free</refname>
|
||||
<refname>free_sized</refname>
|
||||
<refname>free_aligned_sized</refname>
|
||||
<refname>mallocx</refname>
|
||||
<refname>rallocx</refname>
|
||||
<refname>xallocx</refname>
|
||||
|
|
@ -89,6 +91,17 @@
|
|||
<funcdef>void <function>free</function></funcdef>
|
||||
<paramdef>void *<parameter>ptr</parameter></paramdef>
|
||||
</funcprototype>
|
||||
<funcprototype>
|
||||
<funcdef>void <function>free_sized</function></funcdef>
|
||||
<paramdef>void *<parameter>ptr</parameter></paramdef>
|
||||
<paramdef>size_t <parameter>size</parameter></paramdef>
|
||||
</funcprototype>
|
||||
<funcprototype>
|
||||
<funcdef>void <function>free_aligned_sized</function></funcdef>
|
||||
<paramdef>void *<parameter>ptr</parameter></paramdef>
|
||||
<paramdef>size_t <parameter>alignment</parameter></paramdef>
|
||||
<paramdef>size_t <parameter>size</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Non-standard API</title>
|
||||
|
|
@ -227,6 +240,17 @@
|
|||
allocated memory referenced by <parameter>ptr</parameter> to be made
|
||||
available for future allocations. If <parameter>ptr</parameter> is
|
||||
<constant>NULL</constant>, no action occurs.</para>
|
||||
|
||||
<para>The <function>free_sized()</function> function is an extension of
|
||||
<function>free()</function> with a <parameter>size</parameter> parameter
|
||||
to allow the caller to pass in the allocation size as an optimization.
|
||||
</para>
|
||||
|
||||
<para>The <function>free_aligned_sized()</function> function accepts a
|
||||
<parameter>ptr</parameter> which was allocated with a requested
|
||||
<parameter>size</parameter> and <parameter>alignment</parameter>, causing
|
||||
the allocated memory referenced by <parameter>ptr</parameter> to be made
|
||||
available for future allocations.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Non-standard API</title>
|
||||
|
|
@ -424,7 +448,7 @@ for (i = 0; i < nbins; i++) {
|
|||
called repeatedly. General information that never changes during
|
||||
execution can be omitted by specifying <quote>g</quote> as a character
|
||||
within the <parameter>opts</parameter> string. Note that
|
||||
<function>malloc_message()</function> uses the
|
||||
<function>malloc_stats_print()</function> uses the
|
||||
<function>mallctl*()</function> functions internally, so inconsistent
|
||||
statistics can be reported if multiple threads use these functions
|
||||
simultaneously. If <option>--enable-stats</option> is specified during
|
||||
|
|
@ -433,10 +457,11 @@ for (i = 0; i < nbins; i++) {
|
|||
arena statistics, respectively; <quote>b</quote> and <quote>l</quote> can
|
||||
be specified to omit per size class statistics for bins and large objects,
|
||||
respectively; <quote>x</quote> can be specified to omit all mutex
|
||||
statistics. Unrecognized characters are silently ignored. Note that
|
||||
thread caching may prevent some statistics from being completely up to
|
||||
date, since extra locking would be required to merge counters that track
|
||||
thread cache operations.</para>
|
||||
statistics; <quote>e</quote> can be used to omit extent statistics.
|
||||
Unrecognized characters are silently ignored. Note that thread caching
|
||||
may prevent some statistics from being completely up to date, since extra
|
||||
locking would be required to merge counters that track thread cache
|
||||
operations.</para>
|
||||
|
||||
<para>The <function>malloc_usable_size()</function> function
|
||||
returns the usable size of the allocation pointed to by
|
||||
|
|
@ -450,6 +475,24 @@ for (i = 0; i < nbins; i++) {
|
|||
depended on, since such behavior is entirely implementation-dependent.
|
||||
</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Interactions Between the Standard and Non-standard APIs</title>
|
||||
<para>Generally speaking it is permissible to pass pointers obtained from
|
||||
the standard API to the non-standard API and vice versa (e.g. calling
|
||||
<function>free()</function> with a pointer returned by a call to
|
||||
<function>mallocx()</function>, calling <function>sdallocx()</function>
|
||||
with a pointer returned by a call to <function>calloc()</function>).
|
||||
There are however a few exceptions. In keeping with the C23 standard –
|
||||
which forbids calling <function>free_sized()</function> on a pointer
|
||||
returned by <function>aligned_alloc()</function>, mandating that either
|
||||
<function>free_aligned_sized()</function> or <function>free()</function>
|
||||
be used instead – using any combination of the standard and non-standard
|
||||
APIs in an equivalent fashion (i.e. taking a pointer which was allocated
|
||||
with an explicitly requested alignment and attempting to free it via an
|
||||
API that accepts a size hint, without also providing the alignment hint)
|
||||
is likewise forbidden.
|
||||
</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
<refsect1 id="tuning">
|
||||
<title>TUNING</title>
|
||||
|
|
@ -629,7 +672,7 @@ for (i = 0; i < nbins; i++) {
|
|||
</row>
|
||||
<row>
|
||||
<entry>8 KiB</entry>
|
||||
<entry>[40 KiB, 48 KiB, 54 KiB, 64 KiB]</entry>
|
||||
<entry>[40 KiB, 48 KiB, 56 KiB, 64 KiB]</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>16 KiB</entry>
|
||||
|
|
@ -903,6 +946,23 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.confirm_conf">
|
||||
<term>
|
||||
<mallctl>opt.confirm_conf</mallctl>
|
||||
(<type>bool</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Confirm-runtime-options-when-program-starts
|
||||
enabled/disabled. If true, the string specified via
|
||||
<option>--with-malloc-conf</option>, the string pointed to by the
|
||||
global variable <varname>malloc_conf</varname>, the <quote>name</quote>
|
||||
of the file referenced by the symbolic link named
|
||||
<filename class="symlink">/etc/malloc.conf</filename>, and the value of
|
||||
the environment variable <envar>MALLOC_CONF</envar>, will be printed in
|
||||
order. Then, each option being set will be individually printed. This
|
||||
option is disabled by default.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.abort_conf">
|
||||
<term>
|
||||
<mallctl>opt.abort_conf</mallctl>
|
||||
|
|
@ -918,6 +978,22 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.cache_oblivious">
|
||||
<term>
|
||||
<mallctl>opt.cache_oblivious</mallctl>
|
||||
(<type>bool</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Enable / Disable cache-oblivious large allocation
|
||||
alignment, for large requests with no alignment constraints. If this
|
||||
feature is disabled, all large allocations are page-aligned as an
|
||||
implementation artifact, which can severely harm CPU cache utilization.
|
||||
However, the cache-oblivious layout comes at the cost of one extra page
|
||||
per large allocation, which in the most extreme case increases physical
|
||||
memory usage for the 16 KiB size class to 20 KiB. This option is enabled
|
||||
by default.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.metadata_thp">
|
||||
<term>
|
||||
<mallctl>opt.metadata_thp</mallctl>
|
||||
|
|
@ -932,6 +1008,17 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
is <quote>disabled</quote>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.trust_madvise">
|
||||
<term>
|
||||
<mallctl>opt.trust_madvise</mallctl>
|
||||
(<type>bool</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>If true, do not perform runtime check for MADV_DONTNEED,
|
||||
to check that it actually zeros pages. The default is disabled on Linux
|
||||
and enabled elsewhere.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.retain">
|
||||
<term>
|
||||
<mallctl>opt.retain</mallctl>
|
||||
|
|
@ -943,16 +1030,19 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
<citerefentry><refentrytitle>munmap</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> or equivalent (see <link
|
||||
linkend="stats.retained">stats.retained</link> for related details).
|
||||
This option is disabled by default unless discarding virtual memory is
|
||||
known to trigger
|
||||
platform-specific performance problems, e.g. for [64-bit] Linux, which
|
||||
has a quirk in its virtual memory allocation algorithm that causes
|
||||
semi-permanent VM map holes under normal jemalloc operation. Although
|
||||
<citerefentry><refentrytitle>munmap</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> causes issues on 32-bit Linux as
|
||||
well, retaining virtual memory for 32-bit Linux is disabled by default
|
||||
due to the practical possibility of address space exhaustion.
|
||||
</para></listitem>
|
||||
It also makes jemalloc use <citerefentry>
|
||||
<refentrytitle>mmap</refentrytitle><manvolnum>2</manvolnum>
|
||||
</citerefentry> or equivalent in a more greedy way, mapping larger
|
||||
chunks in one go. This option is disabled by default unless discarding
|
||||
virtual memory is known to trigger platform-specific performance
|
||||
problems, namely 1) for [64-bit] Linux, which has a quirk in its virtual
|
||||
memory allocation algorithm that causes semi-permanent VM map holes
|
||||
under normal jemalloc operation; and 2) for [64-bit] Windows, which
|
||||
disallows split / merged regions with
|
||||
<parameter><constant>MEM_RELEASE</constant></parameter>. Although the
|
||||
same issues may present on 32-bit platforms as well, retaining virtual
|
||||
memory for 32-bit Linux and Windows is disabled by default due to the
|
||||
practical possibility of address space exhaustion. </para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.dss">
|
||||
|
|
@ -988,6 +1078,24 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
number of CPUs, or one if there is a single CPU.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.oversize_threshold">
|
||||
<term>
|
||||
<mallctl>opt.oversize_threshold</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>The threshold in bytes of which requests are considered
|
||||
oversize. Allocation requests with greater sizes are fulfilled from a
|
||||
dedicated arena (automatically managed, however not within
|
||||
<literal>narenas</literal>), in order to reduce fragmentation by not
|
||||
mixing huge allocations with small ones. In addition, the decay API
|
||||
guarantees on the extents greater than the specified threshold may be
|
||||
overridden. Note that requests with arena index specified via
|
||||
<constant>MALLOCX_ARENA</constant>, or threads associated with explicit
|
||||
arenas will not be considered. The default threshold is 8MiB. Values
|
||||
not within large size classes disables this feature.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.percpu_arena">
|
||||
<term>
|
||||
<mallctl>opt.percpu_arena</mallctl>
|
||||
|
|
@ -1009,7 +1117,7 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
<varlistentry id="opt.background_thread">
|
||||
<term>
|
||||
<mallctl>opt.background_thread</mallctl>
|
||||
(<type>const bool</type>)
|
||||
(<type>bool</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Internal background worker threads enabled/disabled.
|
||||
|
|
@ -1024,12 +1132,12 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
<varlistentry id="opt.max_background_threads">
|
||||
<term>
|
||||
<mallctl>opt.max_background_threads</mallctl>
|
||||
(<type>const size_t</type>)
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Maximum number of background threads that will be created
|
||||
if <link linkend="background_thread">background_thread</link> is set.
|
||||
Defaults to number of cpus.</para></listitem>
|
||||
Defaults to 4.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.dirty_decay_ms">
|
||||
|
|
@ -1055,7 +1163,9 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
linkend="arena.i.dirty_decay_ms"><mallctl>arena.<i>.dirty_decay_ms</mallctl></link>
|
||||
for related dynamic control options. See <link
|
||||
linkend="opt.muzzy_decay_ms"><mallctl>opt.muzzy_decay_ms</mallctl></link>
|
||||
for a description of muzzy pages.</para></listitem>
|
||||
for a description of muzzy pages. Note that when the <link linkend="opt.oversize_threshold"><mallctl>oversize_threshold</mallctl></link>
|
||||
feature is enabled, the arenas reserved for oversize requests may have
|
||||
its own default decay settings.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.muzzy_decay_ms">
|
||||
|
|
@ -1075,7 +1185,7 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
purged according to a sigmoidal decay curve that starts and ends with
|
||||
zero purge rate. A decay time of 0 causes all unused muzzy pages to be
|
||||
purged immediately upon creation. A decay time of -1 disables purging.
|
||||
The default decay time is 10 seconds. See <link
|
||||
Muzzy decay is disabled by default (with decay time 0). See <link
|
||||
linkend="arenas.muzzy_decay_ms"><mallctl>arenas.muzzy_decay_ms</mallctl></link>
|
||||
and <link
|
||||
linkend="arena.i.muzzy_decay_ms"><mallctl>arena.<i>.muzzy_decay_ms</mallctl></link>
|
||||
|
|
@ -1142,6 +1252,41 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
|
|||
enabled. The default is <quote></quote>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.stats_interval">
|
||||
<term>
|
||||
<mallctl>opt.stats_interval</mallctl>
|
||||
(<type>int64_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Average interval between statistics outputs, as measured
|
||||
in bytes of allocation activity. The actual interval may be sporadic
|
||||
because decentralized event counters are used to avoid synchronization
|
||||
bottlenecks. The output may be triggered on any thread, which then
|
||||
calls <function>malloc_stats_print()</function>. <link
|
||||
linkend="opt.stats_interval_opts"><mallctl>opt.stats_interval_opts</mallctl></link>
|
||||
can be combined to specify output options. By default,
|
||||
interval-triggered stats output is disabled (encoded as
|
||||
-1).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.stats_interval_opts">
|
||||
<term>
|
||||
<mallctl>opt.stats_interval_opts</mallctl>
|
||||
(<type>const char *</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Options (the <parameter>opts</parameter> string) to pass
|
||||
to the <function>malloc_stats_print()</function> for interval based
|
||||
statistics printing (enabled
|
||||
through <link
|
||||
linkend="opt.stats_interval"><mallctl>opt.stats_interval</mallctl></link>). See
|
||||
available options in <link
|
||||
linkend="malloc_stats_print_opts"><function>malloc_stats_print()</function></link>.
|
||||
Has no effect unless <link
|
||||
linkend="opt.stats_interval"><mallctl>opt.stats_interval</mallctl></link> is
|
||||
enabled. The default is <quote></quote>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.junk">
|
||||
<term>
|
||||
<mallctl>opt.junk</mallctl>
|
||||
|
|
@ -1223,21 +1368,23 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
a certain size. Thread-specific caching allows many allocations to be
|
||||
satisfied without performing any thread synchronization, at the cost of
|
||||
increased memory use. See the <link
|
||||
linkend="opt.lg_tcache_max"><mallctl>opt.lg_tcache_max</mallctl></link>
|
||||
linkend="opt.tcache_max"><mallctl>opt.tcache_max</mallctl></link>
|
||||
option for related tuning information. This option is enabled by
|
||||
default.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.lg_tcache_max">
|
||||
<varlistentry id="opt.tcache_max">
|
||||
<term>
|
||||
<mallctl>opt.lg_tcache_max</mallctl>
|
||||
<mallctl>opt.tcache_max</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Maximum size class (log base 2) to cache in the
|
||||
thread-specific cache (tcache). At a minimum, all small size classes
|
||||
are cached, and at a maximum all large size classes are cached. The
|
||||
default maximum is 32 KiB (2^15).</para></listitem>
|
||||
<listitem><para>Maximum size class to cache in the thread-specific cache
|
||||
(tcache). At a minimum, the first size class is cached; and at a
|
||||
maximum, size classes up to 8 MiB can be cached. The default maximum is
|
||||
32 KiB (2^15). As a convenience, this may also be set by specifying
|
||||
lg_tcache_max, which will be taken to be the base-2 logarithm of the
|
||||
setting of tcache_max.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.thp">
|
||||
|
|
@ -1262,6 +1409,17 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
extent hooks.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.prof_bt_max">
|
||||
<term>
|
||||
<mallctl>opt.prof_bt_max</mallctl>
|
||||
(<type>unsigned</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-prof</option>]
|
||||
</term>
|
||||
<listitem><para>Maximum number of stack frames to record in profiling
|
||||
backtraces. The default is 128.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.prof">
|
||||
<term>
|
||||
<mallctl>opt.prof</mallctl>
|
||||
|
|
@ -1301,7 +1459,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
set to the empty string, no automatic dumps will occur; this is
|
||||
primarily useful for disabling the automatic final heap dump (which
|
||||
also disables leak reporting, if enabled). The default prefix is
|
||||
<filename>jeprof</filename>.</para></listitem>
|
||||
<filename>jeprof</filename>. This prefix value can be overridden by
|
||||
<link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.prof_active">
|
||||
|
|
@ -1365,6 +1525,23 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
by default.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.prof_pid_namespace">
|
||||
<term>
|
||||
<mallctl>opt.prof_pid_namespace</mallctl>
|
||||
(<type>bool</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-prof</option>]
|
||||
</term>
|
||||
<listitem><para>Enable adding the pid namespace to the profile
|
||||
filename. Profiles are dumped to files named according to the pattern
|
||||
<filename><prefix>.<pid_namespace>.<pid>.<seq>.i<iseq>.heap</filename>,
|
||||
where <literal><prefix></literal> is controlled by the <link
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link> and
|
||||
<link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
|
||||
options.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.lg_prof_interval">
|
||||
<term>
|
||||
<mallctl>opt.lg_prof_interval</mallctl>
|
||||
|
|
@ -1380,8 +1557,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
<filename><prefix>.<pid>.<seq>.i<iseq>.heap</filename>,
|
||||
where <literal><prefix></literal> is controlled by the
|
||||
<link
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
|
||||
option. By default, interval-triggered profile dumping is disabled
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link> and
|
||||
<link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
|
||||
options. By default, interval-triggered profile dumping is disabled
|
||||
(encoded as -1).
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
|
@ -1413,8 +1591,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
usage to a file named according to the pattern
|
||||
<filename><prefix>.<pid>.<seq>.f.heap</filename>,
|
||||
where <literal><prefix></literal> is controlled by the <link
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
|
||||
option. Note that <function>atexit()</function> may allocate
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link> and
|
||||
<link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
|
||||
options. Note that <function>atexit()</function> may allocate
|
||||
memory during application initialization and then deadlock internally
|
||||
when jemalloc in turn calls <function>atexit()</function>, so
|
||||
this option is not universally usable (though the application can
|
||||
|
|
@ -1435,8 +1614,104 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
<manvolnum>3</manvolnum></citerefentry> function to report memory leaks
|
||||
detected by allocation sampling. See the
|
||||
<link linkend="opt.prof"><mallctl>opt.prof</mallctl></link> option for
|
||||
information on analyzing heap profile output. This option is disabled
|
||||
by default.</para></listitem>
|
||||
information on analyzing heap profile output. Works only when combined
|
||||
with <link linkend="opt.prof_final"><mallctl>opt.prof_final</mallctl>
|
||||
</link>, otherwise does nothing. This option is disabled by default.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.prof_leak_error">
|
||||
<term>
|
||||
<mallctl>opt.prof_leak_error</mallctl>
|
||||
(<type>bool</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-prof</option>]
|
||||
</term>
|
||||
<listitem><para>Similar to <link linkend="opt.prof_leak"><mallctl>
|
||||
opt.prof_leak</mallctl></link>, but makes the process exit with error
|
||||
code 1 if a memory leak is detected. This option supersedes
|
||||
<link linkend="opt.prof_leak"><mallctl>opt.prof_leak</mallctl></link>,
|
||||
meaning that if both are specified, this option takes precedence. When
|
||||
enabled, also enables <link linkend="opt.prof_leak"><mallctl>
|
||||
opt.prof_leak</mallctl></link>. Works only when combined with
|
||||
<link linkend="opt.prof_final"><mallctl>opt.prof_final</mallctl></link>,
|
||||
otherwise does nothing. This option is disabled by default.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.zero_realloc">
|
||||
<term>
|
||||
<mallctl>opt.zero_realloc</mallctl>
|
||||
(<type>const char *</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para> Determines the behavior of
|
||||
<function>realloc()</function> when passed a value of zero for the new
|
||||
size. <quote>alloc</quote> treats this as an allocation of size zero
|
||||
(and returns a non-null result except in case of resource exhaustion).
|
||||
<quote>free</quote> treats this as a deallocation of the pointer, and
|
||||
returns <constant>NULL</constant> without setting
|
||||
<varname>errno</varname>. <quote>abort</quote> aborts the process if
|
||||
zero is passed. The default is <quote>free</quote> on Linux and
|
||||
Windows, and <quote>alloc</quote> elsewhere.</para>
|
||||
|
||||
<para>There is considerable divergence of behaviors across
|
||||
implementations in handling this case. Many have the behavior of
|
||||
<quote>free</quote>. This can introduce security vulnerabilities, since
|
||||
a <constant>NULL</constant> return value indicates failure, and the
|
||||
continued validity of the passed-in pointer (per POSIX and C11).
|
||||
<quote>alloc</quote> is safe, but can cause leaks in programs that
|
||||
expect the common behavior. Programs intended to be portable and
|
||||
leak-free cannot assume either behavior, and must therefore never call
|
||||
realloc with a size of 0. The <quote>abort</quote> option enables these
|
||||
testing this behavior.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.debug_double_free_max_scan">
|
||||
<term>
|
||||
<mallctl>opt.debug_double_free_max_scan</mallctl>
|
||||
(<type>unsigned</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-debug</option>]
|
||||
</term>
|
||||
<listitem><para>Maximum number of cached pointers to scan in the
|
||||
thread cache when checking for double-free errors on deallocation.
|
||||
When debug is enabled, each deallocation into the tcache scans up to
|
||||
this many recently cached pointers to detect whether the same pointer
|
||||
is being freed twice. Setting this to 0 disables the check. This
|
||||
option is set to 0 and has no effect when debug is not enabled. The
|
||||
default is 32.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.disable_large_size_classes">
|
||||
<term>
|
||||
<mallctl>opt.disable_large_size_classes</mallctl>
|
||||
(<type>bool</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>When enabled (the default), large allocations
|
||||
(i.e. allocations of size >= <constant>SC_LARGE_MINCLASS</constant>)
|
||||
are rounded up to the nearest page boundary rather than the nearest
|
||||
large size class. This minimizes memory overhead, especially when
|
||||
using hugepages, at the cost of disabling the standard large size
|
||||
class hierarchy.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.process_madvise_max_batch">
|
||||
<term>
|
||||
<mallctl>opt.process_madvise_max_batch</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Maximum number of memory regions to include in each
|
||||
<citerefentry><refentrytitle>process_madvise</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> batch call. When set to 0
|
||||
(the default), process_madvise is not used, and the standard
|
||||
<citerefentry><refentrytitle>madvise</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> is used instead. Setting this
|
||||
to a positive value enables batched purging via process_madvise, which
|
||||
can reduce the number of system calls needed for
|
||||
purging.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.arena">
|
||||
|
|
@ -1477,7 +1752,8 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
<link
|
||||
linkend="thread.allocated"><mallctl>thread.allocated</mallctl></link>
|
||||
mallctl. This is useful for avoiding the overhead of repeated
|
||||
<function>mallctl*()</function> calls.</para></listitem>
|
||||
<function>mallctl*()</function> calls. Note that the underlying counter
|
||||
should not be modified by the application.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.deallocated">
|
||||
|
|
@ -1504,7 +1780,44 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
<link
|
||||
linkend="thread.deallocated"><mallctl>thread.deallocated</mallctl></link>
|
||||
mallctl. This is useful for avoiding the overhead of repeated
|
||||
<function>mallctl*()</function> calls.</para></listitem>
|
||||
<function>mallctl*()</function> calls. Note that the underlying counter
|
||||
should not be modified by the application.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.peak.read">
|
||||
<term>
|
||||
<mallctl>thread.peak.read</mallctl>
|
||||
(<type>uint64_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Get an approximation of the maximum value of the
|
||||
difference between the number of bytes allocated and the number of bytes
|
||||
deallocated by the calling thread since the last call to <link
|
||||
linkend="thread.peak.reset"><mallctl>thread.peak.reset</mallctl></link>,
|
||||
or since the thread's creation if it has not called <link
|
||||
linkend="thread.peak.reset"><mallctl>thread.peak.reset</mallctl></link>.
|
||||
No guarantees are made about the quality of the approximation, but
|
||||
jemalloc currently endeavors to maintain accuracy to within one hundred
|
||||
kilobytes.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.peak.reset">
|
||||
<term>
|
||||
<mallctl>thread.peak.reset</mallctl>
|
||||
(<type>void</type>)
|
||||
<literal>--</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Resets the counter for net bytes allocated in the calling
|
||||
thread to zero. This affects subsequent calls to <link
|
||||
linkend="thread.peak.read"><mallctl>thread.peak.read</mallctl></link>,
|
||||
but not the values returned by <link
|
||||
linkend="thread.allocated"><mallctl>thread.allocated</mallctl></link>
|
||||
or <link
|
||||
linkend="thread.deallocated"><mallctl>thread.deallocated</mallctl></link>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.tcache.enabled">
|
||||
|
|
@ -1537,6 +1850,47 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
the developer may find manual flushing useful.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.tcache.max">
|
||||
<term>
|
||||
<mallctl>thread.tcache.max</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>rw</literal>
|
||||
</term>
|
||||
<listitem><para>Get or set the maximum cached size class
|
||||
(<varname>tcache_max</varname>) for the calling thread's tcache. The
|
||||
value is clamped to the maximum allowed limit and rounded up to the
|
||||
nearest size class boundary. Changing this value will resize the
|
||||
thread cache accordingly.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.tcache.ncached_max.read_sizeclass">
|
||||
<term>
|
||||
<mallctl>thread.tcache.ncached_max.read_sizeclass</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>rw</literal>
|
||||
</term>
|
||||
<listitem><para>Query the maximum number of cached objects
|
||||
(<varname>ncached_max</varname>) for a given size class in the calling
|
||||
thread's tcache. The size class is passed in via
|
||||
<parameter>newp</parameter>, and the corresponding
|
||||
<varname>ncached_max</varname> is returned via
|
||||
<parameter>oldp</parameter>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.tcache.ncached_max.write">
|
||||
<term>
|
||||
<mallctl>thread.tcache.ncached_max.write</mallctl>
|
||||
(<type>char *</type>)
|
||||
<literal>-w</literal>
|
||||
</term>
|
||||
<listitem><para>Set the maximum number of cached objects
|
||||
(<varname>ncached_max</varname>) for size classes in the calling
|
||||
thread's tcache. The input is a string of pipe-separated settings,
|
||||
where each setting specifies a size range and a count, in the same
|
||||
format as the <mallctl>opt.tcache_ncached_max</mallctl> runtime
|
||||
option.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.prof.name">
|
||||
<term>
|
||||
<mallctl>thread.prof.name</mallctl>
|
||||
|
|
@ -1575,6 +1929,28 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
default.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="thread.idle">
|
||||
<term>
|
||||
<mallctl>thread.idle</mallctl>
|
||||
(<type>void</type>)
|
||||
<literal>--</literal>
|
||||
</term>
|
||||
<listitem><para>Hints to jemalloc that the calling thread will be idle
|
||||
for some nontrivial period of time (say, on the order of seconds), and
|
||||
that doing some cleanup operations may be beneficial. There are no
|
||||
guarantees as to what specific operations will be performed; currently
|
||||
this flushes the caller's tcache and may (according to some heuristic)
|
||||
purge its associated arena.</para>
|
||||
<para>This is not intended to be a general-purpose background activity
|
||||
mechanism, and threads should not wake up multiple times solely to call
|
||||
it. Rather, a thread waiting for a task should do a timed wait first,
|
||||
call <link linkend="thread.idle"><mallctl>thread.idle</mallctl></link>
|
||||
if no task appears in the timeout interval, and then do an untimed wait.
|
||||
For such a background activity mechanism, see
|
||||
<link linkend="background_thread"><mallctl>background_thread</mallctl></link>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="tcache.create">
|
||||
<term>
|
||||
<mallctl>tcache.create</mallctl>
|
||||
|
|
@ -1588,7 +1964,16 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
automatically managed one that is used by default. Each explicit cache
|
||||
can be used by only one thread at a time; the application must assure
|
||||
that this constraint holds.
|
||||
</para>
|
||||
|
||||
<para>If the amount of space supplied for storing the thread-specific
|
||||
cache identifier does not equal
|
||||
<code language="C">sizeof(<type>unsigned</type>)</code>, no
|
||||
thread-specific cache will be created, no data will be written to the
|
||||
space pointed by <parameter>oldp</parameter>, and
|
||||
<parameter>*oldlenp</parameter> will be set to 0.
|
||||
</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="tcache.flush">
|
||||
|
|
@ -1689,6 +2074,24 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
linkend="thread.arena"><mallctl>thread.arena</mallctl></link>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.name">
|
||||
<term>
|
||||
<mallctl>arena.<i>.name</mallctl>
|
||||
(<type>char *</type>)
|
||||
<literal>rw</literal>
|
||||
</term>
|
||||
<listitem><para>Get or set a descriptive name for arena <i>.
|
||||
Arena names can be up to 32 characters long (including the null
|
||||
terminator); longer names are truncated. When reading, the caller
|
||||
passes a pointer to a pre-allocated buffer (of at least 32 bytes) via
|
||||
<parameter>oldp</parameter>, and
|
||||
<parameter>*oldlenp</parameter> must be
|
||||
<code language="C">sizeof(<type>char *</type>)</code>.
|
||||
Arena names are also included in the output of <link
|
||||
linkend="stats_print"><function>malloc_stats_print()</function></link>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.dss">
|
||||
<term>
|
||||
<mallctl>arena.<i>.dss</mallctl>
|
||||
|
|
@ -1763,10 +2166,11 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
|||
to control allocation for arenas explicitly created via <link
|
||||
linkend="arenas.create"><mallctl>arenas.create</mallctl></link> such
|
||||
that all extents originate from an application-supplied extent allocator
|
||||
(by specifying the custom extent hook functions during arena creation),
|
||||
but the automatically created arenas will have already created extents
|
||||
prior to the application having an opportunity to take over extent
|
||||
allocation.</para>
|
||||
(by specifying the custom extent hook functions during arena creation).
|
||||
However, the API guarantees for the automatically created arenas may be
|
||||
relaxed -- hooks set there may be called in a "best effort" fashion; in
|
||||
addition there may be extents created prior to the application having an
|
||||
opportunity to take over extent allocation.</para>
|
||||
|
||||
<programlisting language="C"><![CDATA[
|
||||
typedef extent_hooks_s extent_hooks_t;
|
||||
|
|
@ -2045,6 +2449,18 @@ struct extent_hooks_s {
|
|||
<listitem><para>Page size.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arenas.hugepage">
|
||||
<term>
|
||||
<mallctl>arenas.hugepage</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Hugepage size. This value is also reported in the
|
||||
output of <link
|
||||
linkend="stats_print"><function>malloc_stats_print()</function></link>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arenas.tcache_max">
|
||||
<term>
|
||||
<mallctl>arenas.tcache_max</mallctl>
|
||||
|
|
@ -2127,7 +2543,14 @@ struct extent_hooks_s {
|
|||
</term>
|
||||
<listitem><para>Explicitly create a new arena outside the range of
|
||||
automatically managed arenas, with optionally specified extent hooks,
|
||||
and return the new arena index.</para></listitem>
|
||||
and return the new arena index.</para>
|
||||
|
||||
<para>If the amount of space supplied for storing the arena index does
|
||||
not equal <code language="C">sizeof(<type>unsigned</type>)</code>, no
|
||||
arena will be created, no data will be written to the space pointed by
|
||||
<parameter>oldp</parameter>, and <parameter>*oldlenp</parameter> will
|
||||
be set to 0.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arenas.lookup">
|
||||
|
|
@ -2179,9 +2602,24 @@ struct extent_hooks_s {
|
|||
is specified, to a file according to the pattern
|
||||
<filename><prefix>.<pid>.<seq>.m<mseq>.heap</filename>,
|
||||
where <literal><prefix></literal> is controlled by the
|
||||
<link linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
|
||||
and <link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
|
||||
options.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="prof.prefix">
|
||||
<term>
|
||||
<mallctl>prof.prefix</mallctl>
|
||||
(<type>const char *</type>)
|
||||
<literal>-w</literal>
|
||||
[<option>--enable-prof</option>]
|
||||
</term>
|
||||
<listitem><para>Set the filename prefix for profile dumps. See
|
||||
<link
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
|
||||
option.</para></listitem>
|
||||
for the default setting. This can be useful to differentiate profile
|
||||
dumps such as from forked processes.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="prof.gdump">
|
||||
|
|
@ -2196,8 +2634,9 @@ struct extent_hooks_s {
|
|||
dumped to files named according to the pattern
|
||||
<filename><prefix>.<pid>.<seq>.u<useq>.heap</filename>,
|
||||
where <literal><prefix></literal> is controlled by the <link
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
|
||||
option.</para></listitem>
|
||||
linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link> and
|
||||
<link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
|
||||
options.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="prof.reset">
|
||||
|
|
@ -2241,6 +2680,24 @@ struct extent_hooks_s {
|
|||
option for additional information.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="approximate_stats.active">
|
||||
<term>
|
||||
<mallctl>approximate_stats.active</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Return the total number of bytes in active pages
|
||||
collected in an unsynchronized manner, without requiring an
|
||||
<link linkend="epoch"><mallctl>epoch</mallctl></link> update.
|
||||
As a result, this value should NOT be compared with other
|
||||
stats. For example, the relative ordering between
|
||||
<mallctl>approximate_stats.active</mallctl> and <link
|
||||
linkend="stats.active"><mallctl>stats.active</mallctl></link> or <link
|
||||
linkend="stats.resident"><mallctl>stats.resident</mallctl></link> is
|
||||
not guaranteed. This interface is intended for lightweight monitoring
|
||||
where an approximate value is sufficient.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.allocated">
|
||||
<term>
|
||||
<mallctl>stats.allocated</mallctl>
|
||||
|
|
@ -2354,6 +2811,21 @@ struct extent_hooks_s {
|
|||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.zero_reallocs">
|
||||
<term>
|
||||
<mallctl>stats.zero_reallocs</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Number of times that the <function>realloc()</function>
|
||||
was called with a non-<constant>NULL</constant> pointer argument and a
|
||||
<constant>0</constant> size argument. This is a fundamentally unsafe
|
||||
pattern in portable programs; see <link linkend="opt.zero_realloc">
|
||||
<mallctl>opt.zero_realloc</mallctl></link> for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.background_thread.num_threads">
|
||||
<term>
|
||||
<mallctl>stats.background_thread.num_threads</mallctl>
|
||||
|
|
@ -2465,6 +2937,30 @@ struct extent_hooks_s {
|
|||
counters</link>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.mutexes.prof_thds_data">
|
||||
<term>
|
||||
<mallctl>stats.mutexes.prof_thds_data.{counter}</mallctl>
|
||||
(<type>counter specific type</type>) <literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Statistics on <varname>prof</varname> threads data mutex
|
||||
(global scope; profiling related). <mallctl>{counter}</mallctl> is one
|
||||
of the counters in <link linkend="mutex_counters">mutex profiling
|
||||
counters</link>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.mutexes.prof_dump">
|
||||
<term>
|
||||
<mallctl>stats.mutexes.prof_dump.{counter}</mallctl>
|
||||
(<type>counter specific type</type>) <literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Statistics on <varname>prof</varname> dumping mutex
|
||||
(global scope; profiling related). <mallctl>{counter}</mallctl> is one
|
||||
of the counters in <link linkend="mutex_counters">mutex profiling
|
||||
counters</link>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.mutexes.reset">
|
||||
<term>
|
||||
<mallctl>stats.mutexes.reset</mallctl>
|
||||
|
|
@ -2593,6 +3089,17 @@ struct extent_hooks_s {
|
|||
details.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.extent_avail">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.extent_avail</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Number of allocated (but unused) extent structs in this
|
||||
arena.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.base">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.base</mallctl>
|
||||
|
|
@ -2760,6 +3267,28 @@ struct extent_hooks_s {
|
|||
all bin size classes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.small.nfills">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.small.nfills</mallctl>
|
||||
(<type>uint64_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Cumulative number of tcache fills by all small size
|
||||
classes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.small.nflushes">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.small.nflushes</mallctl>
|
||||
(<type>uint64_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Cumulative number of tcache flushes by all small size
|
||||
classes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.large.allocated">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.large.allocated</mallctl>
|
||||
|
|
@ -2810,6 +3339,28 @@ struct extent_hooks_s {
|
|||
all large size classes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.large.nfills">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.large.nfills</mallctl>
|
||||
(<type>uint64_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Cumulative number of tcache fills by all large size
|
||||
classes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.large.nflushes">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.large.nflushes</mallctl>
|
||||
(<type>uint64_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Cumulative number of tcache flushes by all large size
|
||||
classes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.bins.j.nmalloc">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.bins.<j>.nmalloc</mallctl>
|
||||
|
|
@ -2909,7 +3460,18 @@ struct extent_hooks_s {
|
|||
<listitem><para>Current number of slabs.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.bins.mutex">
|
||||
|
||||
<varlistentry id="stats.arenas.i.bins.j.nonfull_slabs">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.bins.<j>.nonfull_slabs</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para>Current number of nonfull slabs.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.bins.j.mutex">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.bins.<j>.mutex.{counter}</mallctl>
|
||||
(<type>counter specific type</type>) <literal>r-</literal>
|
||||
|
|
@ -2922,6 +3484,30 @@ struct extent_hooks_s {
|
|||
counters</link>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.extents.n">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.extents.<j>.n{extent_type}</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para> Number of extents of the given type in this arena in
|
||||
the bucket corresponding to page size index <j>. The extent type
|
||||
is one of dirty, muzzy, or retained.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.extents.bytes">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.extents.<j>.{extent_type}_bytes</mallctl>
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
[<option>--enable-stats</option>]
|
||||
</term>
|
||||
<listitem><para> Sum of the bytes managed by extents of the given type
|
||||
in this arena in the bucket corresponding to page size index <j>.
|
||||
The extent type is one of dirty, muzzy, or retained.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="stats.arenas.i.lextents.j.nmalloc">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.lextents.<j>.nmalloc</mallctl>
|
||||
|
|
@ -3116,7 +3702,7 @@ heap_v2/524288
|
|||
[...]
|
||||
@ 0x5f86da8 0x5f5a1dc [...] 0x29e4d4e 0xa200316 0xabb2988 [...]
|
||||
t*: 13: 6688 [0: 0]
|
||||
t3: 12: 6496 [0: ]
|
||||
t3: 12: 6496 [0: 0]
|
||||
t99: 1: 192 [0: 0]
|
||||
[...]
|
||||
|
||||
|
|
@ -3127,9 +3713,9 @@ descriptions of the corresponding fields. <programlisting><![CDATA[
|
|||
<heap_profile_format_version>/<mean_sample_interval>
|
||||
<aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
|
||||
[...]
|
||||
<thread_3_aggregate>: <curobjs>: <curbytes>[<cumobjs>: <cumbytes>]
|
||||
<thread_3_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
|
||||
[...]
|
||||
<thread_99_aggregate>: <curobjs>: <curbytes>[<cumobjs>: <cumbytes>]
|
||||
<thread_99_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
|
||||
[...]
|
||||
@ <top_frame> <frame> [...] <frame> <frame> <frame> [...]
|
||||
<backtrace_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
|
||||
|
|
@ -3286,8 +3872,10 @@ MAPPED_LIBRARIES:
|
|||
<listitem><para><parameter>newp</parameter> is not
|
||||
<constant>NULL</constant>, and <parameter>newlen</parameter> is too
|
||||
large or too small. Alternatively, <parameter>*oldlenp</parameter>
|
||||
is too large or too small; in this case as much data as possible
|
||||
are read despite the error.</para></listitem>
|
||||
is too large or too small; when it happens, except for a very few
|
||||
cases explicitly documented otherwise, as much data as possible
|
||||
are read despite the error, with the amount of data read being
|
||||
recorded in <parameter>*oldlenp</parameter>.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorname>ENOENT</errorname></term>
|
||||
|
|
|
|||
145
doc_internal/PROFILING_INTERNALS.md
Normal file
145
doc_internal/PROFILING_INTERNALS.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# jemalloc profiling
|
||||
This describes the mathematical basis behind jemalloc's profiling implementation, as well as the implementation tricks that make it effective. Historically, the jemalloc profiling design simply copied tcmalloc's. The implementation has since diverged, due to both the desire to record additional information, and to correct some biasing bugs.
|
||||
|
||||
Note: this document is markdown with embedded LaTeX; different markdown renderers may not produce the expected output. Viewing with `pandoc -s PROFILING_INTERNALS.md -o PROFILING_INTERNALS.pdf` is recommended.
|
||||
|
||||
## Some tricks in our implementation toolbag
|
||||
|
||||
### Sampling
|
||||
Recording our metadata is quite expensive; we need to walk up the stack to get a stack trace. On top of that, we need to allocate storage to record that stack trace, and stick it somewhere where a profile-dumping call can find it. That call might happen on another thread, so we'll probably need to take a lock to do so. These costs are quite large compared to the average cost of an allocation. To manage this, we'll only sample some fraction of allocations. This will miss some of them, so our data will be incomplete, but we'll try to make up for it. We can tune our sampling rate to balance accuracy and performance.
|
||||
|
||||
### Fast Bernoulli sampling
|
||||
Compared to our fast paths, even a `coinflip(p)` function can be quite expensive. Having to do a random-number generation and some floating point operations would be a sizeable relative cost. However (as pointed out in [[Vitter, 1987](https://dl.acm.org/doi/10.1145/23002.23003)]), if we can orchestrate our algorithm so that many of our `coinflip` calls share their parameter value, we can do better. We can sample from the geometric distribution, and initialize a counter with the result. When the counter hits 0, the `coinflip` function returns true (and reinitializes its internal counter).
|
||||
This can let us do a random-number generation once per (logical) coinflip that comes up heads, rather than once per (logical) coinflip. Since we expect to sample relatively rarely, this can be a large win.
|
||||
|
||||
### Fast-path / slow-path thinking
|
||||
Most programs have a skewed distribution of allocations. Smaller allocations are much more frequent than large ones, but shorter lived and less common as a fraction of program memory. "Small" and "large" are necessarily sort of fuzzy terms, but if we define "small" as "allocations jemalloc puts into slabs" and "large" as the others, then it's not uncommon for small allocations to be hundreds of times more frequent than large ones, but take up around half the amount of heap space as large ones. Moreover, small allocations tend to be much cheaper than large ones (often by a factor of 20-30): they're more likely to hit in thread caches, less likely to have to do an mmap, and cheaper to fill (by the user) once the allocation has been returned.
|
||||
|
||||
## An unbiased estimator of space consumption from (almost) arbitrary sampling strategies
|
||||
Suppose we have a sampling strategy that meets the following criteria:
|
||||
|
||||
- One allocation being sampled is independent of other allocations being sampled.
|
||||
- Each allocation has a non-zero probability of being sampled.
|
||||
|
||||
We can then estimate the bytes in live allocations through some particular stack trace as:
|
||||
|
||||
$$ \sum_i S_i I_i \frac{1}{\mathrm{E}[I_i]} $$
|
||||
|
||||
where the sum ranges over some index variable of live allocations from that stack, $S_i$ is the size of the $i$'th allocation, and $I_i$ is an indicator random variable for whether or not the $i'th$ allocation is sampled. $S_i$ and $\mathrm{E}[I_i]$ are constants (the program allocations are fixed; the random variables are the sampling decisions), so taking the expectation we get
|
||||
|
||||
$$ \sum_i S_i \mathrm{E}[I_i] \frac{1}{\mathrm{E}[I_i]}.$$
|
||||
|
||||
This is of course $\sum_i S_i$, as we want (and, a similar calculation could be done for allocation counts as well).
|
||||
This is a fairly general strategy; note that while we require that sampling decisions be independent of one another's outcomes, they don't have to be independent of previous allocations, total bytes allocated, etc. You can imagine strategies that:
|
||||
|
||||
- Sample allocations at program startup at a higher rate than subsequent allocations
|
||||
- Sample even-indexed allocations more frequently than odd-indexed ones (so long as no allocation has zero sampling probability)
|
||||
- Let threads declare themselves as high-sampling-priority, and sample their allocations at an increased rate.
|
||||
|
||||
These can all be fit into this framework to give an unbiased estimator.
|
||||
|
||||
## Evaluating sampling strategies
|
||||
Not all strategies for picking allocations to sample are equally good, of course. Among unbiased estimators, the lower the variance, the lower the mean squared error. Using the estimator above, the variance is:
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
& \mathrm{Var}[\sum_i S_i I_i \frac{1}{\mathrm{E}[I_i]}] \\
|
||||
=& \sum_i \mathrm{Var}[S_i I_i \frac{1}{\mathrm{E}[I_i]}] \\
|
||||
=& \sum_i \frac{S_i^2}{\mathrm{E}[I_i]^2} \mathrm{Var}[I_i] \\
|
||||
=& \sum_i \frac{S_i^2}{\mathrm{E}[I_i]^2} \mathrm{Var}[I_i] \\
|
||||
=& \sum_i \frac{S_i^2}{\mathrm{E}[I_i]^2} \mathrm{E}[I_i](1 - \mathrm{E}[I_i]) \\
|
||||
=& \sum_i S_i^2 \frac{1 - \mathrm{E}[I_i]}{\mathrm{E}[I_i]}.
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
We can use this formula to compare various strategy choices. All else being equal, lower-variance strategies are better.
|
||||
|
||||
## Possible sampling strategies
|
||||
Because of the desire to avoid the fast-path costs, we'd like to use our Bernoulli trick if possible. There are two obvious counters to use: a coinflip per allocation, and a coinflip per byte allocated.
|
||||
|
||||
### Bernoulli sampling per-allocation
|
||||
An obvious strategy is to pick some large $N$, and give each allocation a $1/N$ chance of being sampled. This would let us use our Bernoulli-via-Geometric trick. Using the formula from above, we can compute the variance as:
|
||||
|
||||
$$ \sum_i S_i^2 \frac{1 - \frac{1}{N}}{\frac{1}{N}} = (N-1) \sum_i S_i^2.$$
|
||||
|
||||
That is, an allocation of size $Z$ contributes a term of $(N-1)Z^2$ to the variance.
|
||||
|
||||
### Bernoulli sampling per-byte
|
||||
Another option we have is to pick some rate $R$, and give each byte a $1/R$ chance of being picked for sampling (at which point we would sample its contained allocation). The chance of an allocation of size $Z$ being sampled, then, is
|
||||
|
||||
$$1-(1-\frac{1}{R})^{Z}$$
|
||||
|
||||
and an allocation of size $Z$ contributes a term of
|
||||
|
||||
$$Z^2 \frac{(1-\frac{1}{R})^{Z}}{1-(1-\frac{1}{R})^{Z}}.$$
|
||||
|
||||
In practical settings, $R$ is large, and so this is well-approximated by
|
||||
|
||||
$$Z^2 \frac{e^{-Z/R}}{1 - e^{-Z/R}} .$$
|
||||
|
||||
Just to get a sense of the dynamics here, let's look at the behavior for various values of $Z$. When $Z$ is small relative to $R$, we can use $e^z \approx 1 + x$, and conclude that the variance contributed by a small-$Z$ allocation is around
|
||||
|
||||
$$Z^2 \frac{1-Z/R}{Z/R} \approx RZ.$$
|
||||
|
||||
When $Z$ is comparable to $R$, the variance term is near $Z^2$ (we have $\frac{e^{-Z/R}}{1 - e^{-Z/R}} = 1$ when $Z/R = \ln 2 \approx 0.693$). When $Z$ is large relative to $R$, the variance term goes to zero.
|
||||
|
||||
## Picking a sampling strategy
|
||||
The fast-path/slow-path dynamics of allocation patterns point us towards the per-byte sampling approach:
|
||||
|
||||
- The quadratic increase in variance per allocation in the first approach is quite costly when heaps have a non-negligible portion of their bytes in those allocations, which is practically often the case.
|
||||
- The Bernoulli-per-byte approach shifts more of its samples towards large allocations, which are already a slow-path.
|
||||
- We drive several tickers (e.g. tcache gc) by bytes allocated, and report bytes-allocated as a user-visible statistic, so we have to do all the necessary bookkeeping anyways.
|
||||
|
||||
Indeed, this is the approach we use in jemalloc. Our heap dumps record the size of the allocation and the sampling rate $R$, and jeprof unbiases by dividing by $1 - e^{-Z/R}$. The framework above would suggest dividing by $1-(1-1/R)^Z$; instead, we use the fact that $R$ is large in practical situations, and so $e^{-Z/R}$ is a good approximation (and faster to compute). (Equivalently, we may also see this as the factor that falls out from viewing sampling as a Poisson process directly).
|
||||
|
||||
## Consequences for heap dump consumers
|
||||
Using this approach means that there are a few things users need to be aware of.
|
||||
|
||||
### Stack counts are not proportional to allocation frequencies
|
||||
If one stack appears twice as often as another, this by itself does not imply that it allocates twice as often. Consider the case in which there are only two types of allocating call stacks in a program. Stack A allocates 8 bytes, and occurs a million times in a program. Stack B allocates 8 MB, and occurs just once in a program. If our sampling rate $R$ is about 1MB, we expect stack A to show up about 8 times, and stack B to show up once. Stack A isn't 8 times more frequent than stack B, though; it's a million times more frequent.
|
||||
|
||||
### Aggregation must be done after unbiasing samples
|
||||
Some tools manually parse heap dump output, and aggregate across stacks (or across program runs) to provide wider-scale data analyses. When doing this aggregation, though, it's important to unbias-and-then-sum, rather than sum-and-then-unbias. Reusing our example from the previous section: suppose we collect heap dumps of the program from 1 million machines. We then have 8 million samples of stack A (8 per machine, each of 8 bytes), and 1 million samples of stack B (1 per machine, each of 8 MB).
|
||||
|
||||
If we sum first then unbias based on this formula: $1 - e^{-Z/R}$ we get:
|
||||
|
||||
$$Z = 8,000,000 * 8 bytes = 64MB$$
|
||||
$$64MB / (1 - e^{-64MB/1MB}) \approx 64MB (Stack A)$$
|
||||
|
||||
$$Z = 1,000,000 * 8MB = 8TB$$
|
||||
$$8TB / (1 - e^{-1TB/1MB}) \approx 8TB (Stack B)$$
|
||||
|
||||
Clearly we are unbiasing by an infinitesimal amount, which dramatically underreports the amount of memory allocated by stack A. Whereas if we unbias first and then sum:
|
||||
|
||||
$$Z = 8 bytes$$
|
||||
$$8 bytes / (1 - e^{-8 bytes/1MB}) \approx 1MB$$
|
||||
$$1MB * 8,000,000 = 8TB (Stack A)$$
|
||||
|
||||
$$Z = 8MB$$
|
||||
$$8MB / (1 - e^{-8MB/1MB}) \approx 8MB$$
|
||||
$$8MB * 1,000,000 = 8TB (Stack B)$$
|
||||
|
||||
## An avenue for future exploration
|
||||
While the framework we laid out above is pretty general, as an engineering decision we're only interested in fairly simple approaches (i.e. ones for which the chance of an allocation being sampled depends only on its size). Our job is then: for each size class $Z$, pick a probability $p_Z$ that an allocation of that size will be sampled. We made some handwave-y references to statistical distributions to justify our choices, but there's no reason we need to pick them that way. Any set of non-zero probabilities is a valid choice.
|
||||
The real limiting factor in our ability to reduce estimator variance is that fact that sampling is expensive; we want to make sure we only do it on a small fraction of allocations. Our goal, then, is to pick the $p_Z$ to minimize variance given some maximum sampling rate $P$. If we define $a_Z$ to be the fraction of allocations of size $Z$, and $l_Z$ to be the fraction of allocations of size $Z$ still alive at the time of a heap dump, then we can phrase this as an optimization problem over the choices of $p_Z$:
|
||||
|
||||
Minimize
|
||||
|
||||
$$ \sum_Z Z^2 l_Z \frac{1-p_Z}{p_Z} $$
|
||||
|
||||
subject to
|
||||
|
||||
$$ \sum_Z a_Z p_Z \leq P $$
|
||||
|
||||
Ignoring a term that doesn't depend on $p_Z$, the objective is minimized whenever
|
||||
|
||||
$$ \sum_Z Z^2 l_Z \frac{1}{p_Z} $$
|
||||
|
||||
is. For a particular program, $l_Z$ and $a_Z$ are just numbers that can be obtained (exactly) from existing stats introspection facilities, and we have a fairly tractable convex optimization problem (it can be framed as a second-order cone program). It would be interesting to evaluate, for various common allocation patterns, how well our current strategy adapts. Do our actual choices for $p_Z$ closely correspond to the optimal ones? How close is the variance of our choices to the variance of the optimal strategy?
|
||||
You can imagine an implementation that actually goes all the way, and makes $p_Z$ selections a tuning parameter. I don't think this is a good use of development time for the foreseeable future; but I do wonder about the answers to some of these questions.
|
||||
|
||||
## Implementation realities
|
||||
|
||||
The nice story above is at least partially a lie. Initially, jeprof (copying its logic from pprof) had the sum-then-unbias error described above. The current version of jemalloc does the unbiasing step on a per-allocation basis internally, so that we're always tracking what the unbiased numbers "should" be. The problem is, actually surfacing those unbiased numbers would require a breaking change to jeprof (and the various already-deployed tools that have copied its logic). Instead, we use a little bit more trickery. Since we know at dump time the numbers we want jeprof to report, we simply choose the values we'll output so that the jeprof numbers will match the true numbers. The math is described in `src/prof_data.c` (where the only cleverness is a change of variables that lets the exponentials fall out).
|
||||
|
||||
This has the effect of making the output of jeprof (and related tools) correct, while making its inputs incorrect. This can be annoying to human readers of raw profiling dump output.
|
||||
1
doc_internal/jemalloc.svg
Normal file
1
doc_internal/jemalloc.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -1,94 +1,125 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_EXTERNS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_stats.h"
|
||||
#include "jemalloc/internal/bin.h"
|
||||
#include "jemalloc/internal/div.h"
|
||||
#include "jemalloc/internal/emap.h"
|
||||
#include "jemalloc/internal/extent_dss.h"
|
||||
#include "jemalloc/internal/hook.h"
|
||||
#include "jemalloc/internal/pages.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
|
||||
/*
|
||||
* When the amount of pages to be purged exceeds this amount, deferred purge
|
||||
* should happen.
|
||||
*/
|
||||
#define ARENA_DEFERRED_PURGE_NPAGES_THRESHOLD UINT64_C(1024)
|
||||
|
||||
extern ssize_t opt_dirty_decay_ms;
|
||||
extern ssize_t opt_muzzy_decay_ms;
|
||||
|
||||
extern percpu_arena_mode_t opt_percpu_arena;
|
||||
extern const char *percpu_arena_mode_names[];
|
||||
extern const char *const percpu_arena_mode_names[];
|
||||
|
||||
extern const uint64_t h_steps[SMOOTHSTEP_NSTEPS];
|
||||
extern malloc_mutex_t arenas_lock;
|
||||
extern div_info_t arena_binind_div_info[SC_NBINS];
|
||||
|
||||
void arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,
|
||||
unsigned *nthreads, const char **dss, ssize_t *dirty_decay_ms,
|
||||
ssize_t *muzzy_decay_ms, size_t *nactive, size_t *ndirty, size_t *nmuzzy);
|
||||
extern emap_t arena_emap_global;
|
||||
|
||||
extern size_t opt_oversize_threshold;
|
||||
extern size_t oversize_threshold;
|
||||
|
||||
extern bool opt_huge_arena_pac_thp;
|
||||
extern pac_thp_t huge_arena_pac_thp;
|
||||
|
||||
/*
|
||||
* arena_bin_offsets[binind] is the offset of the first bin shard for size class
|
||||
* binind.
|
||||
*/
|
||||
extern uint32_t arena_bin_offsets[SC_NBINS];
|
||||
|
||||
void arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
|
||||
size_t *nactive, size_t *ndirty, size_t *nmuzzy);
|
||||
void arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
|
||||
size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,
|
||||
bin_stats_t *bstats, arena_stats_large_t *lstats);
|
||||
void arena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent);
|
||||
#ifdef JEMALLOC_JET
|
||||
size_t arena_slab_regind(extent_t *slab, szind_t binind, const void *ptr);
|
||||
#endif
|
||||
extent_t *arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena,
|
||||
size_t usize, size_t alignment, bool *zero);
|
||||
void arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_t *extent);
|
||||
void arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_t *extent, size_t oldsize);
|
||||
void arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_t *extent, size_t oldsize);
|
||||
ssize_t arena_dirty_decay_ms_get(arena_t *arena);
|
||||
bool arena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);
|
||||
ssize_t arena_muzzy_decay_ms_get(arena_t *arena);
|
||||
bool arena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);
|
||||
void arena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,
|
||||
bool all);
|
||||
void arena_reset(tsd_t *tsd, arena_t *arena);
|
||||
void arena_destroy(tsd_t *tsd, arena_t *arena);
|
||||
void arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
|
||||
cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes);
|
||||
void arena_alloc_junk_small(void *ptr, const bin_info_t *bin_info,
|
||||
bool zero);
|
||||
bin_stats_data_t *bstats, arena_stats_large_t *lstats, pac_estats_t *estats,
|
||||
hpa_shard_stats_t *hpastats);
|
||||
void arena_handle_deferred_work(tsdn_t *tsdn, arena_t *arena);
|
||||
edata_t *arena_extent_alloc_large(
|
||||
tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment, bool zero);
|
||||
void arena_extent_dalloc_large_prep(
|
||||
tsdn_t *tsdn, arena_t *arena, edata_t *edata);
|
||||
void arena_extent_ralloc_large_shrink(
|
||||
tsdn_t *tsdn, arena_t *arena, edata_t *edata, size_t oldusize);
|
||||
void arena_extent_ralloc_large_expand(
|
||||
tsdn_t *tsdn, arena_t *arena, edata_t *edata, size_t oldusize);
|
||||
bool arena_decay_ms_set(
|
||||
tsdn_t *tsdn, arena_t *arena, extent_state_t state, ssize_t decay_ms);
|
||||
ssize_t arena_decay_ms_get(arena_t *arena, extent_state_t state);
|
||||
void arena_decay(
|
||||
tsdn_t *tsdn, arena_t *arena, bool is_background_thread, bool all);
|
||||
uint64_t arena_time_until_deferred(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_do_deferred_work(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_reset(tsd_t *tsd, arena_t *arena);
|
||||
void arena_destroy(tsd_t *tsd, arena_t *arena);
|
||||
cache_bin_sz_t arena_ptr_array_fill_small(tsdn_t *tsdn, arena_t *arena,
|
||||
szind_t binind, cache_bin_ptr_array_t *arr, const cache_bin_sz_t nfill_min,
|
||||
const cache_bin_sz_t nfill_max, cache_bin_stats_t merge_stats);
|
||||
|
||||
typedef void (arena_dalloc_junk_small_t)(void *, const bin_info_t *);
|
||||
extern arena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small;
|
||||
void *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind,
|
||||
bool zero, bool slab);
|
||||
void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||
bool zero, bool slab, tcache_t *tcache);
|
||||
void arena_prof_promote(
|
||||
tsdn_t *tsdn, void *ptr, size_t usize, size_t bumped_usize);
|
||||
void arena_dalloc_promoted(
|
||||
tsdn_t *tsdn, void *ptr, tcache_t *tcache, bool slow_path);
|
||||
void arena_slab_dalloc(tsdn_t *tsdn, arena_t *arena, edata_t *slab);
|
||||
|
||||
void *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size,
|
||||
szind_t ind, bool zero);
|
||||
void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache);
|
||||
void arena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize);
|
||||
void arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
bool slow_path);
|
||||
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_t *extent, void *ptr);
|
||||
void arena_dalloc_small(tsdn_t *tsdn, void *ptr);
|
||||
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, bool zero);
|
||||
void arena_dalloc_small(tsdn_t *tsdn, void *ptr);
|
||||
void arena_ptr_array_flush(tsd_t *tsd, szind_t binind,
|
||||
cache_bin_ptr_array_t *arr, unsigned nflush, bool small,
|
||||
arena_t *stats_arena, cache_bin_stats_t merge_stats);
|
||||
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, bool zero, size_t *newsize);
|
||||
void *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
||||
size_t size, size_t alignment, bool zero, tcache_t *tcache);
|
||||
dss_prec_t arena_dss_prec_get(arena_t *arena);
|
||||
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
|
||||
size_t size, size_t alignment, bool zero, bool slab, tcache_t *tcache,
|
||||
hook_ralloc_args_t *hook_args);
|
||||
dss_prec_t arena_dss_prec_get(arena_t *arena);
|
||||
ehooks_t *arena_get_ehooks(arena_t *arena);
|
||||
extent_hooks_t *arena_set_extent_hooks(
|
||||
tsd_t *tsd, arena_t *arena, extent_hooks_t *extent_hooks);
|
||||
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
|
||||
void arena_name_get(arena_t *arena, char *name);
|
||||
void arena_name_set(arena_t *arena, const char *name);
|
||||
ssize_t arena_dirty_decay_ms_default_get(void);
|
||||
bool arena_dirty_decay_ms_default_set(ssize_t decay_ms);
|
||||
bool arena_dirty_decay_ms_default_set(ssize_t decay_ms);
|
||||
ssize_t arena_muzzy_decay_ms_default_get(void);
|
||||
bool arena_muzzy_decay_ms_default_set(ssize_t decay_ms);
|
||||
bool arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena,
|
||||
size_t *old_limit, size_t *new_limit);
|
||||
bool arena_muzzy_decay_ms_default_set(ssize_t decay_ms);
|
||||
bool arena_retain_grow_limit_get_set(
|
||||
tsd_t *tsd, arena_t *arena, size_t *old_limit, size_t *new_limit);
|
||||
unsigned arena_nthreads_get(arena_t *arena, bool internal);
|
||||
void arena_nthreads_inc(arena_t *arena, bool internal);
|
||||
void arena_nthreads_dec(arena_t *arena, bool internal);
|
||||
size_t arena_extent_sn_next(arena_t *arena);
|
||||
arena_t *arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
|
||||
void arena_boot(void);
|
||||
void arena_prefork0(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork1(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork2(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork3(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork4(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork5(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork6(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork7(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_postfork_parent(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_postfork_child(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_nthreads_inc(arena_t *arena, bool internal);
|
||||
void arena_nthreads_dec(arena_t *arena, bool internal);
|
||||
arena_t *arena_new(tsdn_t *tsdn, unsigned ind, const arena_config_t *config);
|
||||
bool arena_init_huge(tsdn_t *tsdn, arena_t *a0);
|
||||
arena_t *arena_choose_huge(tsd_t *tsd);
|
||||
size_t arena_fill_small_fresh(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
void **ptrs, size_t nfill, bool zero);
|
||||
bool arena_boot(sc_data_t *sc_data, base_t *base, bool hpa);
|
||||
void arena_prefork0(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork1(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork2(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork3(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork4(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork5(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork6(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork7(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork8(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_postfork_parent(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_postfork_child(tsdn_t *tsdn, arena_t *arena);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_EXTERNS_H */
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_A_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_INLINES_A_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_structs.h"
|
||||
|
||||
static inline unsigned
|
||||
arena_ind_get(const arena_t *arena) {
|
||||
return base_ind_get(arena->base);
|
||||
return arena->ind;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -21,37 +24,4 @@ arena_internal_get(arena_t *arena) {
|
|||
return atomic_load_zu(&arena->stats.internal, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
arena_prof_accum(tsdn_t *tsdn, arena_t *arena, uint64_t accumbytes) {
|
||||
cassert(config_prof);
|
||||
|
||||
if (likely(prof_interval == 0 || !prof_active_get_unlocked())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prof_accum_add(tsdn, &arena->prof_accum, accumbytes);
|
||||
}
|
||||
|
||||
static inline void
|
||||
percpu_arena_update(tsd_t *tsd, unsigned cpu) {
|
||||
assert(have_percpu_arena);
|
||||
arena_t *oldarena = tsd_arena_get(tsd);
|
||||
assert(oldarena != NULL);
|
||||
unsigned oldind = arena_ind_get(oldarena);
|
||||
|
||||
if (oldind != cpu) {
|
||||
unsigned newind = cpu;
|
||||
arena_t *newarena = arena_get(tsd_tsdn(tsd), newind, true);
|
||||
assert(newarena != NULL);
|
||||
|
||||
/* Set new arena/tcache associations. */
|
||||
arena_migrate(tsd, oldind, newind);
|
||||
tcache_t *tcache = tcache_get(tsd);
|
||||
if (tcache != NULL) {
|
||||
tcache_arena_reassociate(tsd_tsdn(tsd), tcache,
|
||||
newarena);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_A_H */
|
||||
|
|
|
|||
|
|
@ -1,127 +1,235 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_B_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_INLINES_B_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_externs.h"
|
||||
#include "jemalloc/internal/arena_structs.h"
|
||||
#include "jemalloc/internal/bin_inlines.h"
|
||||
#include "jemalloc/internal/div.h"
|
||||
#include "jemalloc/internal/emap.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_inlines_b.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/large_externs.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/prof_externs.h"
|
||||
#include "jemalloc/internal/prof_structs.h"
|
||||
#include "jemalloc/internal/rtree.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/safety_check.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
#include "jemalloc/internal/tcache_inlines.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE prof_tctx_t *
|
||||
arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
static inline arena_t *
|
||||
arena_get_from_edata(edata_t *edata) {
|
||||
return (arena_t *)atomic_load_p(
|
||||
&arenas[edata_arena_ind_get(edata)], ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
/* Static check. */
|
||||
if (alloc_ctx == NULL) {
|
||||
const extent_t *extent = iealloc(tsdn, ptr);
|
||||
if (unlikely(!extent_slab_get(extent))) {
|
||||
return large_prof_tctx_get(tsdn, extent);
|
||||
}
|
||||
} else {
|
||||
if (unlikely(!alloc_ctx->slab)) {
|
||||
return large_prof_tctx_get(tsdn, iealloc(tsdn, ptr));
|
||||
}
|
||||
JEMALLOC_ALWAYS_INLINE arena_t *
|
||||
arena_choose_maybe_huge(tsd_t *tsd, arena_t *arena, size_t size) {
|
||||
if (arena != NULL) {
|
||||
return arena;
|
||||
}
|
||||
return (prof_tctx_t *)(uintptr_t)1U;
|
||||
|
||||
/*
|
||||
* For huge allocations, use the dedicated huge arena if both are true:
|
||||
* 1) is using auto arena selection (i.e. arena == NULL), and 2) the
|
||||
* thread is not assigned to a manual arena.
|
||||
*/
|
||||
arena_t *tsd_arena = tsd_arena_get(tsd);
|
||||
if (tsd_arena == NULL) {
|
||||
tsd_arena = arena_choose(tsd, NULL);
|
||||
}
|
||||
|
||||
size_t threshold = atomic_load_zu(
|
||||
&tsd_arena->pa_shard.pac.oversize_threshold, ATOMIC_RELAXED);
|
||||
if (unlikely(size >= threshold) && arena_is_auto(tsd_arena)) {
|
||||
return arena_choose_huge(tsd);
|
||||
}
|
||||
|
||||
return tsd_arena;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
large_dalloc_safety_checks(edata_t *edata, const void *ptr, size_t input_size) {
|
||||
if (!config_opt_safety_checks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Eagerly detect double free and sized dealloc bugs for large sizes.
|
||||
* The cost is low enough (as edata will be accessed anyway) to be
|
||||
* enabled all the time.
|
||||
*/
|
||||
if (unlikely(edata == NULL
|
||||
|| edata_state_get(edata) != extent_state_active)) {
|
||||
safety_check_fail(
|
||||
"Invalid deallocation detected: "
|
||||
"pages being freed (%p) not currently active, "
|
||||
"possibly caused by double free bugs.",
|
||||
ptr);
|
||||
return true;
|
||||
}
|
||||
if (unlikely(input_size != edata_usize_get(edata)
|
||||
|| input_size > SC_LARGE_MAXCLASS)) {
|
||||
safety_check_fail_sized_dealloc(/* current_dealloc */ true, ptr,
|
||||
/* true_size */ edata_usize_get(edata), input_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, UNUSED size_t usize,
|
||||
alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
|
||||
arena_prof_info_get(tsd_t *tsd, const void *ptr, emap_alloc_ctx_t *alloc_ctx,
|
||||
prof_info_t *prof_info, bool reset_recent) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
assert(prof_info != NULL);
|
||||
|
||||
edata_t *edata = NULL;
|
||||
bool is_slab;
|
||||
|
||||
/* Static check. */
|
||||
if (alloc_ctx == NULL) {
|
||||
edata = emap_edata_lookup(
|
||||
tsd_tsdn(tsd), &arena_emap_global, ptr);
|
||||
is_slab = edata_slab_get(edata);
|
||||
} else if (unlikely(!(is_slab = alloc_ctx->slab))) {
|
||||
edata = emap_edata_lookup(
|
||||
tsd_tsdn(tsd), &arena_emap_global, ptr);
|
||||
}
|
||||
|
||||
if (unlikely(!is_slab)) {
|
||||
/* edata must have been initialized at this point. */
|
||||
assert(edata != NULL);
|
||||
size_t usize = (alloc_ctx == NULL)
|
||||
? edata_usize_get(edata)
|
||||
: emap_alloc_ctx_usize_get(alloc_ctx);
|
||||
if (reset_recent
|
||||
&& large_dalloc_safety_checks(edata, ptr, usize)) {
|
||||
prof_info->alloc_tctx = PROF_TCTX_SENTINEL;
|
||||
return;
|
||||
}
|
||||
large_prof_info_get(tsd, edata, prof_info, reset_recent);
|
||||
} else {
|
||||
prof_info->alloc_tctx = PROF_TCTX_SENTINEL;
|
||||
/*
|
||||
* No need to set other fields in prof_info; they will never be
|
||||
* accessed if alloc_tctx == PROF_TCTX_SENTINEL.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_prof_tctx_reset(
|
||||
tsd_t *tsd, const void *ptr, emap_alloc_ctx_t *alloc_ctx) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
/* Static check. */
|
||||
if (alloc_ctx == NULL) {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
if (unlikely(!extent_slab_get(extent))) {
|
||||
large_prof_tctx_set(tsdn, extent, tctx);
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsd_tsdn(tsd), &arena_emap_global, ptr);
|
||||
if (unlikely(!edata_slab_get(edata))) {
|
||||
large_prof_tctx_reset(edata);
|
||||
}
|
||||
} else {
|
||||
if (unlikely(!alloc_ctx->slab)) {
|
||||
large_prof_tctx_set(tsdn, iealloc(tsdn, ptr), tctx);
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsd_tsdn(tsd), &arena_emap_global, ptr);
|
||||
large_prof_tctx_reset(edata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, UNUSED prof_tctx_t *tctx) {
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_prof_tctx_reset_sampled(tsd_t *tsd, const void *ptr) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
assert(!extent_slab_get(extent));
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsd_tsdn(tsd), &arena_emap_global, ptr);
|
||||
assert(!edata_slab_get(edata));
|
||||
|
||||
large_prof_tctx_reset(tsdn, extent);
|
||||
large_prof_tctx_reset(edata);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_prof_info_set(
|
||||
tsd_t *tsd, edata_t *edata, prof_tctx_t *tctx, size_t size) {
|
||||
cassert(config_prof);
|
||||
|
||||
assert(!edata_slab_get(edata));
|
||||
large_prof_info_set(edata, tctx, size);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks) {
|
||||
tsd_t *tsd;
|
||||
ticker_t *decay_ticker;
|
||||
|
||||
if (unlikely(tsdn_null(tsdn))) {
|
||||
return;
|
||||
}
|
||||
tsd = tsdn_tsd(tsdn);
|
||||
decay_ticker = decay_ticker_get(tsd, arena_ind_get(arena));
|
||||
if (unlikely(decay_ticker == NULL)) {
|
||||
return;
|
||||
}
|
||||
if (unlikely(ticker_ticks(decay_ticker, nticks))) {
|
||||
tsd_t *tsd = tsdn_tsd(tsdn);
|
||||
/*
|
||||
* We use the ticker_geom_t to avoid having per-arena state in the tsd.
|
||||
* Instead of having a countdown-until-decay timer running for every
|
||||
* arena in every thread, we flip a coin once per tick, whose
|
||||
* probability of coming up heads is 1/nticks; this is effectively the
|
||||
* operation of the ticker_geom_t. Each arena has the same chance of a
|
||||
* coinflip coming up heads (1/ARENA_DECAY_NTICKS_PER_UPDATE), so we can
|
||||
* use a single ticker for all of them.
|
||||
*/
|
||||
ticker_geom_t *decay_ticker = tsd_arena_decay_tickerp_get(tsd);
|
||||
uint64_t *prng_state = tsd_prng_statep_get(tsd);
|
||||
if (unlikely(ticker_geom_ticks(decay_ticker, prng_state, nticks,
|
||||
tsd_reentrancy_level_get(tsd) > 0))) {
|
||||
arena_decay(tsdn, arena, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_decay_tick(tsdn_t *tsdn, arena_t *arena) {
|
||||
malloc_mutex_assert_not_owner(tsdn, &arena->decay_dirty.mtx);
|
||||
malloc_mutex_assert_not_owner(tsdn, &arena->decay_muzzy.mtx);
|
||||
|
||||
arena_decay_ticks(tsdn, arena, 1);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
arena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero,
|
||||
tcache_t *tcache, bool slow_path) {
|
||||
bool slab, tcache_t *tcache, bool slow_path) {
|
||||
assert(!tsdn_null(tsdn) || tcache == NULL);
|
||||
assert(size != 0);
|
||||
|
||||
if (likely(tcache != NULL)) {
|
||||
if (likely(size <= SMALL_MAXCLASS)) {
|
||||
return tcache_alloc_small(tsdn_tsd(tsdn), arena,
|
||||
tcache, size, ind, zero, slow_path);
|
||||
if (likely(slab)) {
|
||||
assert(sz_can_use_slab(size));
|
||||
return tcache_alloc_small(tsdn_tsd(tsdn), arena, tcache,
|
||||
size, ind, zero, slow_path);
|
||||
} else if (likely(ind < tcache_nbins_get(tcache->tcache_slow)
|
||||
&& !tcache_bin_disabled(ind, &tcache->bins[ind],
|
||||
tcache->tcache_slow))) {
|
||||
return tcache_alloc_large(tsdn_tsd(tsdn), arena, tcache,
|
||||
size, ind, zero, slow_path);
|
||||
}
|
||||
if (likely(size <= tcache_maxclass)) {
|
||||
return tcache_alloc_large(tsdn_tsd(tsdn), arena,
|
||||
tcache, size, ind, zero, slow_path);
|
||||
}
|
||||
/* (size > tcache_maxclass) case falls through. */
|
||||
assert(size > tcache_maxclass);
|
||||
/* (size > tcache_max) case falls through. */
|
||||
}
|
||||
|
||||
return arena_malloc_hard(tsdn, arena, size, ind, zero);
|
||||
return arena_malloc_hard(tsdn, arena, size, ind, zero, slab);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE arena_t *
|
||||
arena_aalloc(tsdn_t *tsdn, const void *ptr) {
|
||||
return extent_arena_get(iealloc(tsdn, ptr));
|
||||
edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
|
||||
unsigned arena_ind = edata_arena_ind_get(edata);
|
||||
return (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_salloc(tsdn_t *tsdn, const void *ptr) {
|
||||
assert(ptr != NULL);
|
||||
emap_alloc_ctx_t alloc_ctx;
|
||||
emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr, &alloc_ctx);
|
||||
assert(alloc_ctx.szind != SC_NSIZES);
|
||||
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
|
||||
szind_t szind = rtree_szind_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true);
|
||||
assert(szind != NSIZES);
|
||||
|
||||
return sz_index2size(szind);
|
||||
return emap_alloc_ctx_usize_get(&alloc_ctx);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
|
|
@ -135,60 +243,129 @@ arena_vsalloc(tsdn_t *tsdn, const void *ptr) {
|
|||
* failure.
|
||||
*/
|
||||
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
|
||||
extent_t *extent;
|
||||
szind_t szind;
|
||||
if (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, false, &extent, &szind)) {
|
||||
emap_full_alloc_ctx_t full_alloc_ctx;
|
||||
bool missing = emap_full_alloc_ctx_try_lookup(
|
||||
tsdn, &arena_emap_global, ptr, &full_alloc_ctx);
|
||||
if (missing) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (extent == NULL) {
|
||||
if (full_alloc_ctx.edata == NULL) {
|
||||
return 0;
|
||||
}
|
||||
assert(extent_state_get(extent) == extent_state_active);
|
||||
assert(edata_state_get(full_alloc_ctx.edata) == extent_state_active);
|
||||
/* Only slab members should be looked up via interior pointers. */
|
||||
assert(extent_addr_get(extent) == ptr || extent_slab_get(extent));
|
||||
assert(edata_addr_get(full_alloc_ctx.edata) == ptr
|
||||
|| edata_slab_get(full_alloc_ctx.edata));
|
||||
|
||||
assert(szind != NSIZES);
|
||||
assert(full_alloc_ctx.szind != SC_NSIZES);
|
||||
|
||||
return sz_index2size(szind);
|
||||
return edata_usize_get(full_alloc_ctx.edata);
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_dalloc_large_no_tcache(
|
||||
tsdn_t *tsdn, void *ptr, szind_t szind, size_t usize) {
|
||||
/*
|
||||
* szind is still needed in this function mainly becuase
|
||||
* szind < SC_NBINS determines not only if this is a small alloc,
|
||||
* but also if szind is valid (an inactive extent would have
|
||||
* szind == SC_NSIZES).
|
||||
*/
|
||||
if (config_prof && unlikely(szind < SC_NBINS)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, NULL, true);
|
||||
} else {
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsdn, &arena_emap_global, ptr);
|
||||
if (large_dalloc_safety_checks(edata, ptr, usize)) {
|
||||
/* See the comment in isfree. */
|
||||
return;
|
||||
}
|
||||
large_dalloc(tsdn, edata);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) {
|
||||
assert(ptr != NULL);
|
||||
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,
|
||||
true, &szind, &slab);
|
||||
emap_alloc_ctx_t alloc_ctx;
|
||||
emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr, &alloc_ctx);
|
||||
|
||||
if (config_debug) {
|
||||
extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
|
||||
rtree_ctx, (uintptr_t)ptr, true);
|
||||
assert(szind == extent_szind_get(extent));
|
||||
assert(szind < NSIZES);
|
||||
assert(slab == extent_slab_get(extent));
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsdn, &arena_emap_global, ptr);
|
||||
assert(alloc_ctx.szind == edata_szind_get(edata));
|
||||
assert(alloc_ctx.szind < SC_NSIZES);
|
||||
assert(alloc_ctx.slab == edata_slab_get(edata));
|
||||
assert(emap_alloc_ctx_usize_get(&alloc_ctx)
|
||||
== edata_usize_get(edata));
|
||||
}
|
||||
|
||||
if (likely(slab)) {
|
||||
if (likely(alloc_ctx.slab)) {
|
||||
/* Small allocation. */
|
||||
arena_dalloc_small(tsdn, ptr);
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
arena_dalloc_large_no_tcache(tsdn, ptr, alloc_ctx.szind,
|
||||
emap_alloc_ctx_usize_get(&alloc_ctx));
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_dalloc_large(tsdn_t *tsdn, void *ptr, tcache_t *tcache, szind_t szind,
|
||||
size_t usize, bool slow_path) {
|
||||
assert(!tsdn_null(tsdn) && tcache != NULL);
|
||||
bool is_sample_promoted = config_prof && szind < SC_NBINS;
|
||||
if (unlikely(is_sample_promoted)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, tcache, slow_path);
|
||||
} else {
|
||||
if (szind < tcache_nbins_get(tcache->tcache_slow)
|
||||
&& !tcache_bin_disabled(
|
||||
szind, &tcache->bins[szind], tcache->tcache_slow)) {
|
||||
tcache_dalloc_large(
|
||||
tsdn_tsd(tsdn), tcache, ptr, szind, slow_path);
|
||||
} else {
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsdn, &arena_emap_global, ptr);
|
||||
if (large_dalloc_safety_checks(edata, ptr, usize)) {
|
||||
/* See the comment in isfree. */
|
||||
return;
|
||||
}
|
||||
large_dalloc(tsdn, edata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
arena_tcache_dalloc_small_safety_check(tsdn_t *tsdn, void *ptr) {
|
||||
if (!config_debug) {
|
||||
return false;
|
||||
}
|
||||
edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
|
||||
szind_t binind = edata_szind_get(edata);
|
||||
div_info_t div_info = arena_binind_div_info[binind];
|
||||
/*
|
||||
* Calls the internal function bin_slab_regind_impl because the
|
||||
* safety check does not require a lock.
|
||||
*/
|
||||
size_t regind = bin_slab_regind_impl(&div_info, binind, edata, ptr);
|
||||
slab_data_t *slab_data = edata_slab_data_get(edata);
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
assert(edata_nfree_get(edata) < bin_info->nregs);
|
||||
if (unlikely(!bitmap_get(
|
||||
slab_data->bitmap, &bin_info->bitmap_info, regind))) {
|
||||
safety_check_fail(
|
||||
"Invalid deallocation detected: the pointer being freed (%p) not "
|
||||
"currently active, possibly caused by double free bugs.\n",
|
||||
ptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
alloc_ctx_t *alloc_ctx, bool slow_path) {
|
||||
emap_alloc_ctx_t *caller_alloc_ctx, bool slow_path) {
|
||||
assert(!tsdn_null(tsdn) || tcache == NULL);
|
||||
assert(ptr != NULL);
|
||||
|
||||
|
|
@ -197,158 +374,165 @@ arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
|||
return;
|
||||
}
|
||||
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
rtree_ctx_t *rtree_ctx;
|
||||
if (alloc_ctx != NULL) {
|
||||
szind = alloc_ctx->szind;
|
||||
slab = alloc_ctx->slab;
|
||||
assert(szind != NSIZES);
|
||||
emap_alloc_ctx_t alloc_ctx;
|
||||
if (caller_alloc_ctx != NULL) {
|
||||
alloc_ctx = *caller_alloc_ctx;
|
||||
} else {
|
||||
rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
|
||||
rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true, &szind, &slab);
|
||||
util_assume(tsdn != NULL);
|
||||
emap_alloc_ctx_lookup(
|
||||
tsdn, &arena_emap_global, ptr, &alloc_ctx);
|
||||
}
|
||||
|
||||
if (config_debug) {
|
||||
rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
|
||||
extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
|
||||
rtree_ctx, (uintptr_t)ptr, true);
|
||||
assert(szind == extent_szind_get(extent));
|
||||
assert(szind < NSIZES);
|
||||
assert(slab == extent_slab_get(extent));
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsdn, &arena_emap_global, ptr);
|
||||
assert(alloc_ctx.szind == edata_szind_get(edata));
|
||||
assert(alloc_ctx.szind < SC_NSIZES);
|
||||
assert(alloc_ctx.slab == edata_slab_get(edata));
|
||||
assert(emap_alloc_ctx_usize_get(&alloc_ctx)
|
||||
== edata_usize_get(edata));
|
||||
}
|
||||
|
||||
if (likely(slab)) {
|
||||
if (likely(alloc_ctx.slab)) {
|
||||
/* Small allocation. */
|
||||
tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
|
||||
slow_path);
|
||||
} else {
|
||||
if (szind < nhbins) {
|
||||
if (config_prof && unlikely(szind < NBINS)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, tcache,
|
||||
slow_path);
|
||||
} else {
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,
|
||||
szind, slow_path);
|
||||
}
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
if (arena_tcache_dalloc_small_safety_check(tsdn, ptr)) {
|
||||
return;
|
||||
}
|
||||
tcache_dalloc_small(
|
||||
tsdn_tsd(tsdn), tcache, ptr, alloc_ctx.szind, slow_path);
|
||||
} else {
|
||||
arena_dalloc_large(tsdn, ptr, tcache, alloc_ctx.szind,
|
||||
emap_alloc_ctx_usize_get(&alloc_ctx), slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {
|
||||
assert(ptr != NULL);
|
||||
assert(size <= LARGE_MAXCLASS);
|
||||
assert(size <= SC_LARGE_MAXCLASS);
|
||||
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
emap_alloc_ctx_t alloc_ctx;
|
||||
if (!config_prof || !opt_prof) {
|
||||
/*
|
||||
* There is no risk of being confused by a promoted sampled
|
||||
* object, so base szind and slab on the given size.
|
||||
*/
|
||||
szind = sz_size2index(size);
|
||||
slab = (szind < NBINS);
|
||||
szind_t szind = sz_size2index(size);
|
||||
emap_alloc_ctx_init(
|
||||
&alloc_ctx, szind, (szind < SC_NBINS), size);
|
||||
}
|
||||
|
||||
if ((config_prof && opt_prof) || config_debug) {
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,
|
||||
&rtree_ctx_fallback);
|
||||
emap_alloc_ctx_lookup(
|
||||
tsdn, &arena_emap_global, ptr, &alloc_ctx);
|
||||
|
||||
rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true, &szind, &slab);
|
||||
|
||||
assert(szind == sz_size2index(size));
|
||||
assert((config_prof && opt_prof) || slab == (szind < NBINS));
|
||||
assert(alloc_ctx.szind == sz_size2index(size));
|
||||
assert((config_prof && opt_prof)
|
||||
|| alloc_ctx.slab == (alloc_ctx.szind < SC_NBINS));
|
||||
|
||||
if (config_debug) {
|
||||
extent_t *extent = rtree_extent_read(tsdn,
|
||||
&extents_rtree, rtree_ctx, (uintptr_t)ptr, true);
|
||||
assert(szind == extent_szind_get(extent));
|
||||
assert(slab == extent_slab_get(extent));
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsdn, &arena_emap_global, ptr);
|
||||
assert(alloc_ctx.szind == edata_szind_get(edata));
|
||||
assert(alloc_ctx.slab == edata_slab_get(edata));
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(slab)) {
|
||||
if (likely(alloc_ctx.slab)) {
|
||||
/* Small allocation. */
|
||||
arena_dalloc_small(tsdn, ptr);
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
arena_dalloc_large_no_tcache(tsdn, ptr, alloc_ctx.szind,
|
||||
emap_alloc_ctx_usize_get(&alloc_ctx));
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
alloc_ctx_t *alloc_ctx, bool slow_path) {
|
||||
emap_alloc_ctx_t *caller_alloc_ctx, bool slow_path) {
|
||||
assert(!tsdn_null(tsdn) || tcache == NULL);
|
||||
assert(ptr != NULL);
|
||||
assert(size <= LARGE_MAXCLASS);
|
||||
assert(size <= SC_LARGE_MAXCLASS);
|
||||
|
||||
if (unlikely(tcache == NULL)) {
|
||||
arena_sdalloc_no_tcache(tsdn, ptr, size);
|
||||
return;
|
||||
}
|
||||
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
UNUSED alloc_ctx_t local_ctx;
|
||||
emap_alloc_ctx_t alloc_ctx;
|
||||
if (config_prof && opt_prof) {
|
||||
if (alloc_ctx == NULL) {
|
||||
if (caller_alloc_ctx == NULL) {
|
||||
/* Uncommon case and should be a static check. */
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,
|
||||
&rtree_ctx_fallback);
|
||||
rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true, &local_ctx.szind,
|
||||
&local_ctx.slab);
|
||||
assert(local_ctx.szind == sz_size2index(size));
|
||||
alloc_ctx = &local_ctx;
|
||||
emap_alloc_ctx_lookup(
|
||||
tsdn, &arena_emap_global, ptr, &alloc_ctx);
|
||||
assert(alloc_ctx.szind == sz_size2index(size));
|
||||
assert(emap_alloc_ctx_usize_get(&alloc_ctx) == size);
|
||||
} else {
|
||||
alloc_ctx = *caller_alloc_ctx;
|
||||
}
|
||||
slab = alloc_ctx->slab;
|
||||
szind = alloc_ctx->szind;
|
||||
} else {
|
||||
/*
|
||||
* There is no risk of being confused by a promoted sampled
|
||||
* object, so base szind and slab on the given size.
|
||||
*/
|
||||
szind = sz_size2index(size);
|
||||
slab = (szind < NBINS);
|
||||
alloc_ctx.szind = sz_size2index(size);
|
||||
alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
|
||||
}
|
||||
|
||||
if (config_debug) {
|
||||
rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
|
||||
rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true, &szind, &slab);
|
||||
extent_t *extent = rtree_extent_read(tsdn,
|
||||
&extents_rtree, rtree_ctx, (uintptr_t)ptr, true);
|
||||
assert(szind == extent_szind_get(extent));
|
||||
assert(slab == extent_slab_get(extent));
|
||||
edata_t *edata = emap_edata_lookup(
|
||||
tsdn, &arena_emap_global, ptr);
|
||||
assert(alloc_ctx.szind == edata_szind_get(edata));
|
||||
assert(alloc_ctx.slab == edata_slab_get(edata));
|
||||
emap_alloc_ctx_init(
|
||||
&alloc_ctx, alloc_ctx.szind, alloc_ctx.slab, sz_s2u(size));
|
||||
assert(emap_alloc_ctx_usize_get(&alloc_ctx)
|
||||
== edata_usize_get(edata));
|
||||
}
|
||||
|
||||
if (likely(slab)) {
|
||||
if (likely(alloc_ctx.slab)) {
|
||||
/* Small allocation. */
|
||||
tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
|
||||
slow_path);
|
||||
} else {
|
||||
if (szind < nhbins) {
|
||||
if (config_prof && unlikely(szind < NBINS)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, tcache,
|
||||
slow_path);
|
||||
} else {
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn),
|
||||
tcache, ptr, szind, slow_path);
|
||||
}
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
if (arena_tcache_dalloc_small_safety_check(tsdn, ptr)) {
|
||||
return;
|
||||
}
|
||||
tcache_dalloc_small(
|
||||
tsdn_tsd(tsdn), tcache, ptr, alloc_ctx.szind, slow_path);
|
||||
} else {
|
||||
arena_dalloc_large(tsdn, ptr, tcache, alloc_ctx.szind,
|
||||
sz_s2u(size), slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_cache_oblivious_randomize(
|
||||
tsdn_t *tsdn, arena_t *arena, edata_t *edata, size_t alignment) {
|
||||
assert(edata_base_get(edata) == edata_addr_get(edata));
|
||||
|
||||
if (alignment < PAGE) {
|
||||
unsigned lg_range = LG_PAGE
|
||||
- lg_floor(CACHELINE_CEILING(alignment));
|
||||
size_t r;
|
||||
if (!tsdn_null(tsdn)) {
|
||||
tsd_t *tsd = tsdn_tsd(tsdn);
|
||||
r = (size_t)prng_lg_range_u64(
|
||||
tsd_prng_statep_get(tsd), lg_range);
|
||||
} else {
|
||||
uint64_t stack_value = (uint64_t)(uintptr_t)&r;
|
||||
r = (size_t)prng_lg_range_u64(&stack_value, lg_range);
|
||||
}
|
||||
uintptr_t random_offset = ((uintptr_t)r)
|
||||
<< (LG_PAGE - lg_range);
|
||||
edata->e_addr = (void *)((byte_t *)edata->e_addr
|
||||
+ random_offset);
|
||||
assert(ALIGNMENT_ADDR2BASE(edata->e_addr, alignment)
|
||||
== edata->e_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bin_t *
|
||||
arena_get_bin(arena_t *arena, szind_t binind, unsigned binshard) {
|
||||
bin_t *shard0 = (bin_t *)((byte_t *)arena + arena_bin_offsets[binind]);
|
||||
return shard0 + binshard;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_B_H */
|
||||
|
|
|
|||
|
|
@ -1,51 +1,47 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_STATS_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_STATS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/lockedint.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/mutex_prof.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/pa.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/*
|
||||
* In those architectures that support 64-bit atomics, we use atomic updates for
|
||||
* our 64-bit values. Otherwise, we use a plain uint64_t and synchronize
|
||||
* externally.
|
||||
*/
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
typedef atomic_u64_t arena_stats_u64_t;
|
||||
#else
|
||||
/* Must hold the arena stats mutex while reading atomically. */
|
||||
typedef uint64_t arena_stats_u64_t;
|
||||
#endif
|
||||
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
|
||||
typedef struct arena_stats_large_s arena_stats_large_t;
|
||||
struct arena_stats_large_s {
|
||||
/*
|
||||
* Total number of allocation/deallocation requests served directly by
|
||||
* the arena.
|
||||
* Total number of large allocation/deallocation requests served directly
|
||||
* by the arena.
|
||||
*/
|
||||
arena_stats_u64_t nmalloc;
|
||||
arena_stats_u64_t ndalloc;
|
||||
locked_u64_t nmalloc;
|
||||
locked_u64_t ndalloc;
|
||||
|
||||
/*
|
||||
* Total large active bytes (allocated - deallocated) served directly
|
||||
* by the arena.
|
||||
*/
|
||||
locked_u64_t active_bytes;
|
||||
|
||||
/*
|
||||
* Number of allocation requests that correspond to this size class.
|
||||
* This includes requests served by tcache, though tcache only
|
||||
* periodically merges into this counter.
|
||||
*/
|
||||
arena_stats_u64_t nrequests; /* Partially derived. */
|
||||
locked_u64_t nrequests; /* Partially derived. */
|
||||
/*
|
||||
* Number of tcache fills / flushes for large (similarly, periodically
|
||||
* merged). Note that there is no large tcache batch-fill currently
|
||||
* (i.e. only fill 1 at a time); however flush may be batched.
|
||||
*/
|
||||
locked_u64_t nfills; /* Partially derived. */
|
||||
locked_u64_t nflushes; /* Partially derived. */
|
||||
|
||||
/* Current number of allocations of this size class. */
|
||||
size_t curlextents; /* Derived. */
|
||||
};
|
||||
|
||||
typedef struct arena_stats_decay_s arena_stats_decay_t;
|
||||
struct arena_stats_decay_s {
|
||||
/* Total number of purge sweeps. */
|
||||
arena_stats_u64_t npurge;
|
||||
/* Total number of madvise calls made. */
|
||||
arena_stats_u64_t nmadvise;
|
||||
/* Total number of pages purged. */
|
||||
arena_stats_u64_t purged;
|
||||
size_t curlextents; /* Derived. */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -55,183 +51,73 @@ struct arena_stats_decay_s {
|
|||
*/
|
||||
typedef struct arena_stats_s arena_stats_t;
|
||||
struct arena_stats_s {
|
||||
#ifndef JEMALLOC_ATOMIC_U64
|
||||
malloc_mutex_t mtx;
|
||||
#endif
|
||||
|
||||
/* Number of bytes currently mapped, excluding retained memory. */
|
||||
atomic_zu_t mapped; /* Partially derived. */
|
||||
LOCKEDINT_MTX_DECLARE(mtx)
|
||||
|
||||
/*
|
||||
* Number of unused virtual memory bytes currently retained. Retained
|
||||
* bytes are technically mapped (though always decommitted or purged),
|
||||
* but they are excluded from the mapped statistic (above).
|
||||
* resident includes the base stats -- that's why it lives here and not
|
||||
* in pa_shard_stats_t.
|
||||
*/
|
||||
atomic_zu_t retained; /* Derived. */
|
||||
size_t base; /* Derived. */
|
||||
size_t metadata_edata; /* Derived. */
|
||||
size_t metadata_rtree; /* Derived. */
|
||||
size_t resident; /* Derived. */
|
||||
size_t metadata_thp; /* Derived. */
|
||||
size_t mapped; /* Derived. */
|
||||
|
||||
arena_stats_decay_t decay_dirty;
|
||||
arena_stats_decay_t decay_muzzy;
|
||||
atomic_zu_t internal;
|
||||
|
||||
atomic_zu_t base; /* Derived. */
|
||||
atomic_zu_t internal;
|
||||
atomic_zu_t resident; /* Derived. */
|
||||
atomic_zu_t metadata_thp;
|
||||
size_t allocated_large; /* Derived. */
|
||||
uint64_t nmalloc_large; /* Derived. */
|
||||
uint64_t ndalloc_large; /* Derived. */
|
||||
uint64_t nfills_large; /* Derived. */
|
||||
uint64_t nflushes_large; /* Derived. */
|
||||
uint64_t nrequests_large; /* Derived. */
|
||||
|
||||
atomic_zu_t allocated_large; /* Derived. */
|
||||
arena_stats_u64_t nmalloc_large; /* Derived. */
|
||||
arena_stats_u64_t ndalloc_large; /* Derived. */
|
||||
arena_stats_u64_t nrequests_large; /* Derived. */
|
||||
/*
|
||||
* The stats logically owned by the pa_shard in the same arena. This
|
||||
* lives here only because it's convenient for the purposes of the ctl
|
||||
* module -- it only knows about the single arena_stats.
|
||||
*/
|
||||
pa_shard_stats_t pa_shard_stats;
|
||||
|
||||
/* Number of bytes cached in tcache associated with this arena. */
|
||||
atomic_zu_t tcache_bytes; /* Derived. */
|
||||
size_t tcache_bytes; /* Derived. */
|
||||
size_t tcache_stashed_bytes; /* Derived. */
|
||||
|
||||
mutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes];
|
||||
|
||||
/* One element for each large size class. */
|
||||
arena_stats_large_t lstats[NSIZES - NBINS];
|
||||
arena_stats_large_t lstats[SC_NSIZES - SC_NBINS];
|
||||
|
||||
/* Arena uptime. */
|
||||
nstime_t uptime;
|
||||
nstime_t uptime;
|
||||
};
|
||||
|
||||
static inline bool
|
||||
arena_stats_init(UNUSED tsdn_t *tsdn, arena_stats_t *arena_stats) {
|
||||
arena_stats_init(tsdn_t *tsdn, arena_stats_t *arena_stats) {
|
||||
if (config_debug) {
|
||||
for (size_t i = 0; i < sizeof(arena_stats_t); i++) {
|
||||
assert(((char *)arena_stats)[i] == 0);
|
||||
}
|
||||
}
|
||||
#ifndef JEMALLOC_ATOMIC_U64
|
||||
if (malloc_mutex_init(&arena_stats->mtx, "arena_stats",
|
||||
WITNESS_RANK_ARENA_STATS, malloc_mutex_rank_exclusive)) {
|
||||
if (LOCKEDINT_MTX_INIT(arena_stats->mtx, "arena_stats",
|
||||
WITNESS_RANK_ARENA_STATS, malloc_mutex_rank_exclusive)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
/* Memory is zeroed, so there is no need to clear stats. */
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_lock(tsdn_t *tsdn, arena_stats_t *arena_stats) {
|
||||
#ifndef JEMALLOC_ATOMIC_U64
|
||||
malloc_mutex_lock(tsdn, &arena_stats->mtx);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_unlock(tsdn_t *tsdn, arena_stats_t *arena_stats) {
|
||||
#ifndef JEMALLOC_ATOMIC_U64
|
||||
malloc_mutex_unlock(tsdn, &arena_stats->mtx);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
arena_stats_read_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
arena_stats_u64_t *p) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
return atomic_load_u64(p, ATOMIC_RELAXED);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
return *p;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_add_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
arena_stats_u64_t *p, uint64_t x) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
atomic_fetch_add_u64(p, x, ATOMIC_RELAXED);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
*p += x;
|
||||
#endif
|
||||
}
|
||||
|
||||
UNUSED static inline void
|
||||
arena_stats_sub_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
arena_stats_u64_t *p, uint64_t x) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
UNUSED uint64_t r = atomic_fetch_sub_u64(p, x, ATOMIC_RELAXED);
|
||||
assert(r - x <= r);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
*p -= x;
|
||||
assert(*p + x >= *p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-atomically sets *dst += src. *dst needs external synchronization.
|
||||
* This lets us avoid the cost of a fetch_add when its unnecessary (note that
|
||||
* the types here are atomic).
|
||||
*/
|
||||
static inline void
|
||||
arena_stats_accum_u64(arena_stats_u64_t *dst, uint64_t src) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
uint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);
|
||||
atomic_store_u64(dst, src + cur_dst, ATOMIC_RELAXED);
|
||||
#else
|
||||
*dst += src;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
arena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
return atomic_load_zu(p, ATOMIC_RELAXED);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
return atomic_load_zu(p, ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,
|
||||
size_t x) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
atomic_fetch_add_zu(p, x, ATOMIC_RELAXED);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
size_t cur = atomic_load_zu(p, ATOMIC_RELAXED);
|
||||
atomic_store_zu(p, cur + x, ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_sub_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,
|
||||
size_t x) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
UNUSED size_t r = atomic_fetch_sub_zu(p, x, ATOMIC_RELAXED);
|
||||
assert(r - x <= r);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
size_t cur = atomic_load_zu(p, ATOMIC_RELAXED);
|
||||
atomic_store_zu(p, cur - x, ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Like the _u64 variant, needs an externally synchronized *dst. */
|
||||
static inline void
|
||||
arena_stats_accum_zu(atomic_zu_t *dst, size_t src) {
|
||||
size_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);
|
||||
atomic_store_zu(dst, src + cur_dst, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_large_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
arena_stats_large_flush_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
szind_t szind, uint64_t nrequests) {
|
||||
arena_stats_lock(tsdn, arena_stats);
|
||||
arena_stats_add_u64(tsdn, arena_stats, &arena_stats->lstats[szind -
|
||||
NBINS].nrequests, nrequests);
|
||||
arena_stats_unlock(tsdn, arena_stats);
|
||||
LOCKEDINT_MTX_LOCK(tsdn, arena_stats->mtx);
|
||||
arena_stats_large_t *lstats = &arena_stats->lstats[szind - SC_NBINS];
|
||||
locked_inc_u64(tsdn, LOCKEDINT_MTX(arena_stats->mtx),
|
||||
&lstats->nrequests, nrequests);
|
||||
locked_inc_u64(
|
||||
tsdn, LOCKEDINT_MTX(arena_stats->mtx), &lstats->nflushes, 1);
|
||||
LOCKEDINT_MTX_UNLOCK(tsdn, arena_stats->mtx);
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_mapped_add(tsdn_t *tsdn, arena_stats_t *arena_stats, size_t size) {
|
||||
arena_stats_lock(tsdn, arena_stats);
|
||||
arena_stats_add_zu(tsdn, arena_stats, &arena_stats->mapped, size);
|
||||
arena_stats_unlock(tsdn, arena_stats);
|
||||
}
|
||||
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_STATS_H */
|
||||
|
|
|
|||
111
include/jemalloc/internal/arena_structs.h
Normal file
111
include/jemalloc/internal/arena_structs.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_STRUCTS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_stats.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/bin.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
#include "jemalloc/internal/counter.h"
|
||||
#include "jemalloc/internal/ecache.h"
|
||||
#include "jemalloc/internal/edata_cache.h"
|
||||
#include "jemalloc/internal/extent_dss.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/nstime.h"
|
||||
#include "jemalloc/internal/pa.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
|
||||
struct arena_s {
|
||||
/*
|
||||
* Number of threads currently assigned to this arena. Each thread has
|
||||
* two distinct assignments, one for application-serving allocation, and
|
||||
* the other for internal metadata allocation. Internal metadata must
|
||||
* not be allocated from arenas explicitly created via the arenas.create
|
||||
* mallctl, because the arena.<i>.reset mallctl indiscriminately
|
||||
* discards all allocations for the affected arena.
|
||||
*
|
||||
* 0: Application allocation.
|
||||
* 1: Internal metadata allocation.
|
||||
*
|
||||
* Synchronization: atomic.
|
||||
*/
|
||||
atomic_u_t nthreads[2];
|
||||
|
||||
/* Next bin shard for binding new threads. Synchronization: atomic. */
|
||||
atomic_u_t binshard_next;
|
||||
|
||||
/*
|
||||
* When percpu_arena is enabled, to amortize the cost of reading /
|
||||
* updating the current CPU id, track the most recent thread accessing
|
||||
* this arena, and only read CPU if there is a mismatch.
|
||||
*/
|
||||
tsdn_t *last_thd;
|
||||
|
||||
/* Synchronization: internal. */
|
||||
arena_stats_t stats;
|
||||
|
||||
/*
|
||||
* Lists of tcaches and cache_bin_array_descriptors for extant threads
|
||||
* associated with this arena. Stats from these are merged
|
||||
* incrementally, and at exit if opt_stats_print is enabled.
|
||||
*
|
||||
* Synchronization: tcache_ql_mtx.
|
||||
*/
|
||||
ql_head(tcache_slow_t) tcache_ql;
|
||||
ql_head(cache_bin_array_descriptor_t) cache_bin_array_descriptor_ql;
|
||||
malloc_mutex_t tcache_ql_mtx;
|
||||
|
||||
/*
|
||||
* Represents a dss_prec_t, but atomically.
|
||||
*
|
||||
* Synchronization: atomic.
|
||||
*/
|
||||
atomic_u_t dss_prec;
|
||||
|
||||
/*
|
||||
* Extant large allocations.
|
||||
*
|
||||
* Synchronization: large_mtx.
|
||||
*/
|
||||
edata_list_active_t large;
|
||||
/* Synchronizes all large allocation/update/deallocation. */
|
||||
malloc_mutex_t large_mtx;
|
||||
|
||||
/* The page-level allocator shard this arena uses. */
|
||||
pa_shard_t pa_shard;
|
||||
|
||||
/*
|
||||
* A cached copy of base->ind. This can get accessed on hot paths;
|
||||
* looking it up in base requires an extra pointer hop / cache miss.
|
||||
*/
|
||||
unsigned ind;
|
||||
|
||||
/*
|
||||
* Base allocator, from which arena metadata are allocated.
|
||||
*
|
||||
* Synchronization: internal.
|
||||
*/
|
||||
base_t *base;
|
||||
/* Used to determine uptime. Read-only after initialization. */
|
||||
nstime_t create_time;
|
||||
|
||||
/* The name of the arena. */
|
||||
char name[ARENA_NAME_LEN];
|
||||
|
||||
/*
|
||||
* The arena is allocated alongside its bins; really this is a
|
||||
* dynamically sized array determined by the binshard settings.
|
||||
* Enforcing cacheline-alignment to minimize the number of cachelines
|
||||
* touched on the hot paths.
|
||||
*/
|
||||
JEMALLOC_WARN_ON_USAGE(
|
||||
"Do not use this field directly. "
|
||||
"Use `arena_get_bin` instead.")
|
||||
JEMALLOC_ALIGNED(CACHELINE)
|
||||
bin_t all_bins[0];
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_H */
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H
|
||||
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
|
||||
struct arena_slab_data_s {
|
||||
/* Per region allocated/deallocated bitmap. */
|
||||
bitmap_t bitmap[BITMAP_GROUPS_MAX];
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H */
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H
|
||||
|
||||
#include "jemalloc/internal/arena_stats.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/bin.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
#include "jemalloc/internal/extent_dss.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/nstime.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/smoothstep.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
|
||||
struct arena_decay_s {
|
||||
/* Synchronizes all non-atomic fields. */
|
||||
malloc_mutex_t mtx;
|
||||
/*
|
||||
* True if a thread is currently purging the extents associated with
|
||||
* this decay structure.
|
||||
*/
|
||||
bool purging;
|
||||
/*
|
||||
* Approximate time in milliseconds from the creation of a set of unused
|
||||
* dirty pages until an equivalent set of unused dirty pages is purged
|
||||
* and/or reused.
|
||||
*/
|
||||
atomic_zd_t time_ms;
|
||||
/* time / SMOOTHSTEP_NSTEPS. */
|
||||
nstime_t interval;
|
||||
/*
|
||||
* Time at which the current decay interval logically started. We do
|
||||
* not actually advance to a new epoch until sometime after it starts
|
||||
* because of scheduling and computation delays, and it is even possible
|
||||
* to completely skip epochs. In all cases, during epoch advancement we
|
||||
* merge all relevant activity into the most recently recorded epoch.
|
||||
*/
|
||||
nstime_t epoch;
|
||||
/* Deadline randomness generator. */
|
||||
uint64_t jitter_state;
|
||||
/*
|
||||
* Deadline for current epoch. This is the sum of interval and per
|
||||
* epoch jitter which is a uniform random variable in [0..interval).
|
||||
* Epochs always advance by precise multiples of interval, but we
|
||||
* randomize the deadline to reduce the likelihood of arenas purging in
|
||||
* lockstep.
|
||||
*/
|
||||
nstime_t deadline;
|
||||
/*
|
||||
* Number of unpurged pages at beginning of current epoch. During epoch
|
||||
* advancement we use the delta between arena->decay_*.nunpurged and
|
||||
* extents_npages_get(&arena->extents_*) to determine how many dirty
|
||||
* pages, if any, were generated.
|
||||
*/
|
||||
size_t nunpurged;
|
||||
/*
|
||||
* Trailing log of how many unused dirty pages were generated during
|
||||
* each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last
|
||||
* element is the most recent epoch. Corresponding epoch times are
|
||||
* relative to epoch.
|
||||
*/
|
||||
size_t backlog[SMOOTHSTEP_NSTEPS];
|
||||
|
||||
/*
|
||||
* Pointer to associated stats. These stats are embedded directly in
|
||||
* the arena's stats due to how stats structures are shared between the
|
||||
* arena and ctl code.
|
||||
*
|
||||
* Synchronization: Same as associated arena's stats field. */
|
||||
arena_stats_decay_t *stats;
|
||||
/* Peak number of pages in associated extents. Used for debug only. */
|
||||
uint64_t ceil_npages;
|
||||
};
|
||||
|
||||
struct arena_s {
|
||||
/*
|
||||
* Number of threads currently assigned to this arena. Each thread has
|
||||
* two distinct assignments, one for application-serving allocation, and
|
||||
* the other for internal metadata allocation. Internal metadata must
|
||||
* not be allocated from arenas explicitly created via the arenas.create
|
||||
* mallctl, because the arena.<i>.reset mallctl indiscriminately
|
||||
* discards all allocations for the affected arena.
|
||||
*
|
||||
* 0: Application allocation.
|
||||
* 1: Internal metadata allocation.
|
||||
*
|
||||
* Synchronization: atomic.
|
||||
*/
|
||||
atomic_u_t nthreads[2];
|
||||
|
||||
/*
|
||||
* When percpu_arena is enabled, to amortize the cost of reading /
|
||||
* updating the current CPU id, track the most recent thread accessing
|
||||
* this arena, and only read CPU if there is a mismatch.
|
||||
*/
|
||||
tsdn_t *last_thd;
|
||||
|
||||
/* Synchronization: internal. */
|
||||
arena_stats_t stats;
|
||||
|
||||
/*
|
||||
* Lists of tcaches and cache_bin_array_descriptors for extant threads
|
||||
* associated with this arena. Stats from these are merged
|
||||
* incrementally, and at exit if opt_stats_print is enabled.
|
||||
*
|
||||
* Synchronization: tcache_ql_mtx.
|
||||
*/
|
||||
ql_head(tcache_t) tcache_ql;
|
||||
ql_head(cache_bin_array_descriptor_t) cache_bin_array_descriptor_ql;
|
||||
malloc_mutex_t tcache_ql_mtx;
|
||||
|
||||
/* Synchronization: internal. */
|
||||
prof_accum_t prof_accum;
|
||||
uint64_t prof_accumbytes;
|
||||
|
||||
/*
|
||||
* PRNG state for cache index randomization of large allocation base
|
||||
* pointers.
|
||||
*
|
||||
* Synchronization: atomic.
|
||||
*/
|
||||
atomic_zu_t offset_state;
|
||||
|
||||
/*
|
||||
* Extent serial number generator state.
|
||||
*
|
||||
* Synchronization: atomic.
|
||||
*/
|
||||
atomic_zu_t extent_sn_next;
|
||||
|
||||
/*
|
||||
* Represents a dss_prec_t, but atomically.
|
||||
*
|
||||
* Synchronization: atomic.
|
||||
*/
|
||||
atomic_u_t dss_prec;
|
||||
|
||||
/*
|
||||
* Number of pages in active extents.
|
||||
*
|
||||
* Synchronization: atomic.
|
||||
*/
|
||||
atomic_zu_t nactive;
|
||||
|
||||
/*
|
||||
* Extant large allocations.
|
||||
*
|
||||
* Synchronization: large_mtx.
|
||||
*/
|
||||
extent_list_t large;
|
||||
/* Synchronizes all large allocation/update/deallocation. */
|
||||
malloc_mutex_t large_mtx;
|
||||
|
||||
/*
|
||||
* Collections of extents that were previously allocated. These are
|
||||
* used when allocating extents, in an attempt to re-use address space.
|
||||
*
|
||||
* Synchronization: internal.
|
||||
*/
|
||||
extents_t extents_dirty;
|
||||
extents_t extents_muzzy;
|
||||
extents_t extents_retained;
|
||||
|
||||
/*
|
||||
* Decay-based purging state, responsible for scheduling extent state
|
||||
* transitions.
|
||||
*
|
||||
* Synchronization: internal.
|
||||
*/
|
||||
arena_decay_t decay_dirty; /* dirty --> muzzy */
|
||||
arena_decay_t decay_muzzy; /* muzzy --> retained */
|
||||
|
||||
/*
|
||||
* Next extent size class in a growing series to use when satisfying a
|
||||
* request via the extent hooks (only if opt_retain). This limits the
|
||||
* number of disjoint virtual memory ranges so that extent merging can
|
||||
* be effective even if multiple arenas' extent allocation requests are
|
||||
* highly interleaved.
|
||||
*
|
||||
* retain_grow_limit is the max allowed size ind to expand (unless the
|
||||
* required size is greater). Default is no limit, and controlled
|
||||
* through mallctl only.
|
||||
*
|
||||
* Synchronization: extent_grow_mtx
|
||||
*/
|
||||
pszind_t extent_grow_next;
|
||||
pszind_t retain_grow_limit;
|
||||
malloc_mutex_t extent_grow_mtx;
|
||||
|
||||
/*
|
||||
* Available extent structures that were allocated via
|
||||
* base_alloc_extent().
|
||||
*
|
||||
* Synchronization: extent_avail_mtx.
|
||||
*/
|
||||
extent_tree_t extent_avail;
|
||||
malloc_mutex_t extent_avail_mtx;
|
||||
|
||||
/*
|
||||
* bins is used to store heaps of free regions.
|
||||
*
|
||||
* Synchronization: internal.
|
||||
*/
|
||||
bin_t bins[NBINS];
|
||||
|
||||
/*
|
||||
* Base allocator, from which arena metadata are allocated.
|
||||
*
|
||||
* Synchronization: internal.
|
||||
*/
|
||||
base_t *base;
|
||||
/* Used to determine uptime. Read-only after initialization. */
|
||||
nstime_t create_time;
|
||||
};
|
||||
|
||||
/* Used in conjunction with tsd for fast arena-related context lookup. */
|
||||
struct arena_tdata_s {
|
||||
ticker_t decay_ticker;
|
||||
};
|
||||
|
||||
/* Used to pass rtree lookup context down the path. */
|
||||
struct alloc_ctx_s {
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H */
|
||||
|
|
@ -1,43 +1,60 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ARENA_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_TYPES_H
|
||||
|
||||
/* Maximum number of regions in one slab. */
|
||||
#define LG_SLAB_MAXREGS (LG_PAGE - LG_TINY_MIN)
|
||||
#define SLAB_MAXREGS (1U << LG_SLAB_MAXREGS)
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/* Default decay times in milliseconds. */
|
||||
#define DIRTY_DECAY_MS_DEFAULT ZD(10 * 1000)
|
||||
#define MUZZY_DECAY_MS_DEFAULT ZD(10 * 1000)
|
||||
#define DIRTY_DECAY_MS_DEFAULT ZD(10 * 1000)
|
||||
#define MUZZY_DECAY_MS_DEFAULT (0)
|
||||
/* Number of event ticks between time checks. */
|
||||
#define DECAY_NTICKS_PER_UPDATE 1000
|
||||
#define ARENA_DECAY_NTICKS_PER_UPDATE 1000
|
||||
/* Maximum length of the arena name. */
|
||||
#define ARENA_NAME_LEN 32
|
||||
|
||||
typedef struct arena_slab_data_s arena_slab_data_t;
|
||||
typedef struct arena_decay_s arena_decay_t;
|
||||
typedef struct arena_s arena_t;
|
||||
typedef struct arena_tdata_s arena_tdata_t;
|
||||
typedef struct alloc_ctx_s alloc_ctx_t;
|
||||
|
||||
typedef enum {
|
||||
percpu_arena_mode_names_base = 0, /* Used for options processing. */
|
||||
percpu_arena_mode_names_base = 0, /* Used for options processing. */
|
||||
|
||||
/*
|
||||
* *_uninit are used only during bootstrapping, and must correspond
|
||||
* to initialized variant plus percpu_arena_mode_enabled_base.
|
||||
*/
|
||||
percpu_arena_uninit = 0,
|
||||
per_phycpu_arena_uninit = 1,
|
||||
percpu_arena_uninit = 0,
|
||||
per_phycpu_arena_uninit = 1,
|
||||
|
||||
/* All non-disabled modes must come after percpu_arena_disabled. */
|
||||
percpu_arena_disabled = 2,
|
||||
percpu_arena_disabled = 2,
|
||||
|
||||
percpu_arena_mode_names_limit = 3, /* Used for options processing. */
|
||||
percpu_arena_mode_names_limit = 3, /* Used for options processing. */
|
||||
percpu_arena_mode_enabled_base = 3,
|
||||
|
||||
percpu_arena = 3,
|
||||
per_phycpu_arena = 4 /* Hyper threads share arena. */
|
||||
percpu_arena = 3,
|
||||
per_phycpu_arena = 4 /* Hyper threads share arena. */
|
||||
} percpu_arena_mode_t;
|
||||
|
||||
#define PERCPU_ARENA_ENABLED(m) ((m) >= percpu_arena_mode_enabled_base)
|
||||
#define PERCPU_ARENA_DEFAULT percpu_arena_disabled
|
||||
#define PERCPU_ARENA_ENABLED(m) ((m) >= percpu_arena_mode_enabled_base)
|
||||
#define PERCPU_ARENA_DEFAULT percpu_arena_disabled
|
||||
|
||||
/*
|
||||
* When allocation_size >= oversize_threshold, use the dedicated huge arena
|
||||
* (unless have explicitly spicified arena index). 0 disables the feature.
|
||||
*/
|
||||
#define OVERSIZE_THRESHOLD_DEFAULT (8 << 20)
|
||||
|
||||
struct arena_config_s {
|
||||
/* extent hooks to be used for the arena */
|
||||
extent_hooks_t *extent_hooks;
|
||||
|
||||
/*
|
||||
* Use extent hooks for metadata (base) allocations when true.
|
||||
*/
|
||||
bool metadata_use_hooks;
|
||||
};
|
||||
|
||||
typedef struct arena_config_s arena_config_t;
|
||||
|
||||
extern const arena_config_t arena_config_default;
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_TYPES_H */
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/malloc_io.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
|
||||
|
|
@ -6,51 +7,57 @@
|
|||
* assertion failure.
|
||||
*/
|
||||
#ifndef assert
|
||||
#define assert(e) do { \
|
||||
if (unlikely(config_debug && !(e))) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Failed assertion: \"%s\"\n", \
|
||||
__FILE__, __LINE__, #e); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
# define assert(e) \
|
||||
do { \
|
||||
if (unlikely(config_debug && !(e))) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Failed assertion: \"%s\"\n", \
|
||||
__FILE__, __LINE__, #e); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef not_reached
|
||||
#define not_reached() do { \
|
||||
if (config_debug) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Unreachable code reached\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
# define not_reached() \
|
||||
do { \
|
||||
if (config_debug) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Unreachable code reached\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef not_implemented
|
||||
#define not_implemented() do { \
|
||||
if (config_debug) { \
|
||||
malloc_printf("<jemalloc>: %s:%d: Not implemented\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
# define not_implemented() \
|
||||
do { \
|
||||
if (config_debug) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: %s:%d: Not implemented\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef assert_not_implemented
|
||||
#define assert_not_implemented(e) do { \
|
||||
if (unlikely(config_debug && !(e))) { \
|
||||
not_implemented(); \
|
||||
} \
|
||||
} while (0)
|
||||
# define assert_not_implemented(e) \
|
||||
do { \
|
||||
if (unlikely(config_debug && !(e))) { \
|
||||
not_implemented(); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* Use to assert a particular configuration, e.g., cassert(config_debug). */
|
||||
#ifndef cassert
|
||||
#define cassert(c) do { \
|
||||
if (unlikely(!(c))) { \
|
||||
not_reached(); \
|
||||
} \
|
||||
} while (0)
|
||||
# define cassert(c) \
|
||||
do { \
|
||||
if (unlikely(!(c))) { \
|
||||
not_reached(); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,20 +1,29 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ATOMIC_H
|
||||
#define JEMALLOC_INTERNAL_ATOMIC_H
|
||||
|
||||
#define ATOMIC_INLINE static inline
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
|
||||
#define JEMALLOC_U8_ATOMICS
|
||||
#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS)
|
||||
# include "jemalloc/internal/atomic_gcc_atomic.h"
|
||||
# include "jemalloc/internal/atomic_gcc_atomic.h"
|
||||
# if !defined(JEMALLOC_GCC_U8_ATOMIC_ATOMICS)
|
||||
# undef JEMALLOC_U8_ATOMICS
|
||||
# endif
|
||||
#elif defined(JEMALLOC_GCC_SYNC_ATOMICS)
|
||||
# include "jemalloc/internal/atomic_gcc_sync.h"
|
||||
# include "jemalloc/internal/atomic_gcc_sync.h"
|
||||
# if !defined(JEMALLOC_GCC_U8_SYNC_ATOMICS)
|
||||
# undef JEMALLOC_U8_ATOMICS
|
||||
# endif
|
||||
#elif defined(_MSC_VER)
|
||||
# include "jemalloc/internal/atomic_msvc.h"
|
||||
# include "jemalloc/internal/atomic_msvc.h"
|
||||
#elif defined(JEMALLOC_C11_ATOMICS)
|
||||
# include "jemalloc/internal/atomic_c11.h"
|
||||
# include "jemalloc/internal/atomic_c11.h"
|
||||
#else
|
||||
# error "Don't have atomics implemented on this platform."
|
||||
# error "Don't have atomics implemented on this platform."
|
||||
#endif
|
||||
|
||||
#define ATOMIC_INLINE JEMALLOC_ALWAYS_INLINE
|
||||
|
||||
/*
|
||||
* This header gives more or less a backport of C11 atomics. The user can write
|
||||
* JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_sizeof_type); to generate
|
||||
|
|
@ -44,12 +53,30 @@
|
|||
#define ATOMIC_ACQ_REL atomic_memory_order_acq_rel
|
||||
#define ATOMIC_SEQ_CST atomic_memory_order_seq_cst
|
||||
|
||||
/*
|
||||
* Another convenience -- simple atomic helper functions.
|
||||
*/
|
||||
#define JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(type, short_type, lg_size) \
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, lg_size) \
|
||||
ATOMIC_INLINE void atomic_load_add_store_##short_type( \
|
||||
atomic_##short_type##_t *a, type inc) { \
|
||||
type oldval = atomic_load_##short_type(a, ATOMIC_RELAXED); \
|
||||
type newval = oldval + inc; \
|
||||
atomic_store_##short_type(a, newval, ATOMIC_RELAXED); \
|
||||
} \
|
||||
ATOMIC_INLINE void atomic_load_sub_store_##short_type( \
|
||||
atomic_##short_type##_t *a, type inc) { \
|
||||
type oldval = atomic_load_##short_type(a, ATOMIC_RELAXED); \
|
||||
type newval = oldval - inc; \
|
||||
atomic_store_##short_type(a, newval, ATOMIC_RELAXED); \
|
||||
}
|
||||
|
||||
/*
|
||||
* Not all platforms have 64-bit atomics. If we do, this #define exposes that
|
||||
* fact.
|
||||
*/
|
||||
#if (LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)
|
||||
# define JEMALLOC_ATOMIC_U64
|
||||
# define JEMALLOC_ATOMIC_U64
|
||||
#endif
|
||||
|
||||
JEMALLOC_GENERATE_ATOMICS(void *, p, LG_SIZEOF_PTR)
|
||||
|
|
@ -60,16 +87,20 @@ JEMALLOC_GENERATE_ATOMICS(void *, p, LG_SIZEOF_PTR)
|
|||
*/
|
||||
JEMALLOC_GENERATE_ATOMICS(bool, b, 0)
|
||||
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(unsigned, u, LG_SIZEOF_INT)
|
||||
JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(unsigned, u, LG_SIZEOF_INT)
|
||||
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)
|
||||
JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(int, i, LG_SIZEOF_INT)
|
||||
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
|
||||
JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)
|
||||
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)
|
||||
JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
|
||||
|
||||
JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(uint8_t, u8, 0)
|
||||
|
||||
JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(uint32_t, u32, 2)
|
||||
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(uint64_t, u64, 3)
|
||||
JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(uint64_t, u64, 3)
|
||||
#endif
|
||||
|
||||
#undef ATOMIC_INLINE
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ATOMIC_C11_H
|
||||
#define JEMALLOC_INTERNAL_ATOMIC_C11_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define ATOMIC_INIT(...) ATOMIC_VAR_INIT(__VA_ARGS__)
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
|
||||
#define atomic_fence atomic_thread_fence
|
||||
|
||||
/* clang-format off */
|
||||
#define JEMALLOC_GENERATE_ATOMICS(type, short_type, \
|
||||
/* unused */ lg_size) \
|
||||
typedef _Atomic(type) atomic_##short_type##_t; \
|
||||
|
|
@ -58,40 +60,35 @@ atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
|
|||
return atomic_compare_exchange_strong_explicit(a, expected, \
|
||||
desired, success_mo, failure_mo); \
|
||||
}
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* Integral types have some special operations available that non-integral ones
|
||||
* lack.
|
||||
*/
|
||||
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, \
|
||||
/* unused */ lg_size) \
|
||||
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_add_##short_type(atomic_##short_type##_t *a, \
|
||||
type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_add_explicit(a, val, mo); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_sub_##short_type(atomic_##short_type##_t *a, \
|
||||
type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_sub_explicit(a, val, mo); \
|
||||
} \
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_and_##short_type(atomic_##short_type##_t *a, \
|
||||
type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_and_explicit(a, val, mo); \
|
||||
} \
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_or_##short_type(atomic_##short_type##_t *a, \
|
||||
type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_or_explicit(a, val, mo); \
|
||||
} \
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_xor_##short_type(atomic_##short_type##_t *a, \
|
||||
type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_xor_explicit(a, val, mo); \
|
||||
}
|
||||
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_add_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_add_explicit(a, val, mo); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_sub_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_sub_explicit(a, val, mo); \
|
||||
} \
|
||||
ATOMIC_INLINE type atomic_fetch_and_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_and_explicit(a, val, mo); \
|
||||
} \
|
||||
ATOMIC_INLINE type atomic_fetch_or_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_or_explicit(a, val, mo); \
|
||||
} \
|
||||
ATOMIC_INLINE type atomic_fetch_xor_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return atomic_fetch_xor_explicit(a, val, mo); \
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ATOMIC_C11_H */
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H
|
||||
#define JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
|
||||
#define ATOMIC_INIT(...) {__VA_ARGS__}
|
||||
#define ATOMIC_INLINE JEMALLOC_ALWAYS_INLINE
|
||||
|
||||
#define ATOMIC_INIT(...) \
|
||||
{ __VA_ARGS__ }
|
||||
|
||||
typedef enum {
|
||||
atomic_memory_order_relaxed,
|
||||
|
|
@ -36,92 +40,82 @@ atomic_fence(atomic_memory_order_t mo) {
|
|||
__atomic_thread_fence(atomic_enum_to_builtin(mo));
|
||||
}
|
||||
|
||||
#define JEMALLOC_GENERATE_ATOMICS(type, short_type, \
|
||||
/* unused */ lg_size) \
|
||||
typedef struct { \
|
||||
type repr; \
|
||||
} atomic_##short_type##_t; \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_load_##short_type(const atomic_##short_type##_t *a, \
|
||||
atomic_memory_order_t mo) { \
|
||||
type result; \
|
||||
__atomic_load(&a->repr, &result, atomic_enum_to_builtin(mo)); \
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE void \
|
||||
atomic_store_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
__atomic_store(&a->repr, &val, atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
type result; \
|
||||
__atomic_exchange(&a->repr, &val, &result, \
|
||||
atomic_enum_to_builtin(mo)); \
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
return __atomic_compare_exchange(&a->repr, expected, &desired, \
|
||||
true, atomic_enum_to_builtin(success_mo), \
|
||||
atomic_enum_to_builtin(failure_mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
return __atomic_compare_exchange(&a->repr, expected, &desired, \
|
||||
false, \
|
||||
atomic_enum_to_builtin(success_mo), \
|
||||
atomic_enum_to_builtin(failure_mo)); \
|
||||
}
|
||||
#define JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
typedef struct { \
|
||||
type repr; \
|
||||
} atomic_##short_type##_t; \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_load_##short_type( \
|
||||
const atomic_##short_type##_t *a, atomic_memory_order_t mo) { \
|
||||
type result; \
|
||||
__atomic_load(&a->repr, &result, atomic_enum_to_builtin(mo)); \
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE void atomic_store_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
__atomic_store(&a->repr, &val, atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_exchange_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
type result; \
|
||||
__atomic_exchange( \
|
||||
&a->repr, &val, &result, atomic_enum_to_builtin(mo)); \
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool atomic_compare_exchange_weak_##short_type( \
|
||||
atomic_##short_type##_t *a, UNUSED type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
return __atomic_compare_exchange(&a->repr, expected, &desired, \
|
||||
true, atomic_enum_to_builtin(success_mo), \
|
||||
atomic_enum_to_builtin(failure_mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool atomic_compare_exchange_strong_##short_type( \
|
||||
atomic_##short_type##_t *a, UNUSED type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
return __atomic_compare_exchange(&a->repr, expected, &desired, \
|
||||
false, atomic_enum_to_builtin(success_mo), \
|
||||
atomic_enum_to_builtin(failure_mo)); \
|
||||
}
|
||||
|
||||
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_add_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_add( \
|
||||
&a->repr, val, atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_sub_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_sub( \
|
||||
&a->repr, val, atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_and_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_and( \
|
||||
&a->repr, val, atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_or_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_or( \
|
||||
&a->repr, val, atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_xor_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_xor( \
|
||||
&a->repr, val, atomic_enum_to_builtin(mo)); \
|
||||
}
|
||||
|
||||
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, \
|
||||
/* unused */ lg_size) \
|
||||
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_add(&a->repr, val, \
|
||||
atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_sub(&a->repr, val, \
|
||||
atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_and(&a->repr, val, \
|
||||
atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_or(&a->repr, val, \
|
||||
atomic_enum_to_builtin(mo)); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __atomic_fetch_xor(&a->repr, val, \
|
||||
atomic_enum_to_builtin(mo)); \
|
||||
}
|
||||
#undef ATOMIC_INLINE
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H
|
||||
#define JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H
|
||||
|
||||
#define ATOMIC_INIT(...) {__VA_ARGS__}
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
|
||||
#define ATOMIC_INLINE JEMALLOC_ALWAYS_INLINE
|
||||
|
||||
#define ATOMIC_INIT(...) \
|
||||
{ __VA_ARGS__ }
|
||||
|
||||
typedef enum {
|
||||
atomic_memory_order_relaxed,
|
||||
|
|
@ -25,11 +30,13 @@ atomic_fence(atomic_memory_order_t mo) {
|
|||
return;
|
||||
}
|
||||
asm volatile("" ::: "memory");
|
||||
# if defined(__i386__) || defined(__x86_64__)
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
/* This is implicit on x86. */
|
||||
# elif defined(__ppc__)
|
||||
#elif defined(__ppc64__)
|
||||
asm volatile("lwsync");
|
||||
# elif defined(__sparc__) && defined(__arch64__)
|
||||
#elif defined(__ppc__)
|
||||
asm volatile("sync");
|
||||
#elif defined(__sparc__) && defined(__arch64__)
|
||||
if (mo == atomic_memory_order_acquire) {
|
||||
asm volatile("membar #LoadLoad | #LoadStore");
|
||||
} else if (mo == atomic_memory_order_release) {
|
||||
|
|
@ -37,9 +44,9 @@ atomic_fence(atomic_memory_order_t mo) {
|
|||
} else {
|
||||
asm volatile("membar #LoadLoad | #LoadStore | #StoreStore");
|
||||
}
|
||||
# else
|
||||
#else
|
||||
__sync_synchronize();
|
||||
# endif
|
||||
#endif
|
||||
asm volatile("" ::: "memory");
|
||||
}
|
||||
|
||||
|
|
@ -62,25 +69,25 @@ atomic_fence(atomic_memory_order_t mo) {
|
|||
|
||||
ATOMIC_INLINE void
|
||||
atomic_pre_sc_load_fence() {
|
||||
# if defined(__i386__) || defined(__x86_64__) || \
|
||||
(defined(__sparc__) && defined(__arch64__))
|
||||
#if defined(__i386__) || defined(__x86_64__) \
|
||||
|| (defined(__sparc__) && defined(__arch64__))
|
||||
atomic_fence(atomic_memory_order_relaxed);
|
||||
# else
|
||||
#else
|
||||
atomic_fence(atomic_memory_order_seq_cst);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE void
|
||||
atomic_post_sc_store_fence() {
|
||||
# if defined(__i386__) || defined(__x86_64__) || \
|
||||
(defined(__sparc__) && defined(__arch64__))
|
||||
#if defined(__i386__) || defined(__x86_64__) \
|
||||
|| (defined(__sparc__) && defined(__arch64__))
|
||||
atomic_fence(atomic_memory_order_seq_cst);
|
||||
# else
|
||||
#else
|
||||
atomic_fence(atomic_memory_order_relaxed);
|
||||
# endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#define JEMALLOC_GENERATE_ATOMICS(type, short_type, \
|
||||
/* unused */ lg_size) \
|
||||
typedef struct { \
|
||||
|
|
@ -113,8 +120,8 @@ atomic_store_##short_type(atomic_##short_type##_t *a, \
|
|||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
/* \
|
||||
* Because of FreeBSD, we care about gcc 4.2, which doesn't have\
|
||||
* an atomic exchange builtin. We fake it with a CAS loop. \
|
||||
|
|
@ -129,8 +136,9 @@ atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
|||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type prev = __sync_val_compare_and_swap(&a->repr, *expected, \
|
||||
desired); \
|
||||
if (prev == *expected) { \
|
||||
|
|
@ -142,8 +150,9 @@ atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
|||
} \
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type prev = __sync_val_compare_and_swap(&a->repr, *expected, \
|
||||
desired); \
|
||||
if (prev == *expected) { \
|
||||
|
|
@ -153,39 +162,36 @@ atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
|
|||
return false; \
|
||||
} \
|
||||
}
|
||||
/* clang-format on */
|
||||
|
||||
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, \
|
||||
/* unused */ lg_size) \
|
||||
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_add(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_sub(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_and(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_or(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_xor(&a->repr, val); \
|
||||
}
|
||||
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size) \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_add_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_add(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_sub_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_sub(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_and_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_and(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_or_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_or(&a->repr, val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_fetch_xor_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return __sync_fetch_and_xor(&a->repr, val); \
|
||||
}
|
||||
|
||||
#undef ATOMIC_INLINE
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ATOMIC_MSVC_H
|
||||
#define JEMALLOC_INTERNAL_ATOMIC_MSVC_H
|
||||
|
||||
#define ATOMIC_INIT(...) {__VA_ARGS__}
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
|
||||
#define ATOMIC_INLINE JEMALLOC_ALWAYS_INLINE
|
||||
|
||||
#define ATOMIC_INIT(...) \
|
||||
{ __VA_ARGS__ }
|
||||
|
||||
typedef enum {
|
||||
atomic_memory_order_relaxed,
|
||||
|
|
@ -11,109 +16,106 @@ typedef enum {
|
|||
atomic_memory_order_seq_cst
|
||||
} atomic_memory_order_t;
|
||||
|
||||
typedef char atomic_repr_0_t;
|
||||
typedef short atomic_repr_1_t;
|
||||
typedef long atomic_repr_2_t;
|
||||
typedef char atomic_repr_0_t;
|
||||
typedef short atomic_repr_1_t;
|
||||
typedef long atomic_repr_2_t;
|
||||
typedef __int64 atomic_repr_3_t;
|
||||
|
||||
ATOMIC_INLINE void
|
||||
atomic_fence(atomic_memory_order_t mo) {
|
||||
_ReadWriteBarrier();
|
||||
# if defined(_M_ARM) || defined(_M_ARM64)
|
||||
#if defined(_M_ARM) || defined(_M_ARM64)
|
||||
/* ARM needs a barrier for everything but relaxed. */
|
||||
if (mo != atomic_memory_order_relaxed) {
|
||||
MemoryBarrier();
|
||||
}
|
||||
# elif defined(_M_IX86) || defined (_M_X64)
|
||||
#elif defined(_M_IX86) || defined(_M_X64)
|
||||
/* x86 needs a barrier only for seq_cst. */
|
||||
if (mo == atomic_memory_order_seq_cst) {
|
||||
MemoryBarrier();
|
||||
}
|
||||
# else
|
||||
# error "Don't know how to create atomics for this platform for MSVC."
|
||||
# endif
|
||||
#else
|
||||
# error "Don't know how to create atomics for this platform for MSVC."
|
||||
#endif
|
||||
_ReadWriteBarrier();
|
||||
}
|
||||
|
||||
#define ATOMIC_INTERLOCKED_REPR(lg_size) atomic_repr_ ## lg_size ## _t
|
||||
#define ATOMIC_INTERLOCKED_REPR(lg_size) atomic_repr_##lg_size##_t
|
||||
|
||||
#define ATOMIC_CONCAT(a, b) ATOMIC_RAW_CONCAT(a, b)
|
||||
#define ATOMIC_RAW_CONCAT(a, b) a ## b
|
||||
#define ATOMIC_RAW_CONCAT(a, b) a##b
|
||||
|
||||
#define ATOMIC_INTERLOCKED_NAME(base_name, lg_size) ATOMIC_CONCAT( \
|
||||
base_name, ATOMIC_INTERLOCKED_SUFFIX(lg_size))
|
||||
#define ATOMIC_INTERLOCKED_NAME(base_name, lg_size) \
|
||||
ATOMIC_CONCAT(base_name, ATOMIC_INTERLOCKED_SUFFIX(lg_size))
|
||||
|
||||
#define ATOMIC_INTERLOCKED_SUFFIX(lg_size) \
|
||||
ATOMIC_CONCAT(ATOMIC_INTERLOCKED_SUFFIX_, lg_size)
|
||||
#define ATOMIC_INTERLOCKED_SUFFIX(lg_size) \
|
||||
ATOMIC_CONCAT(ATOMIC_INTERLOCKED_SUFFIX_, lg_size)
|
||||
|
||||
#define ATOMIC_INTERLOCKED_SUFFIX_0 8
|
||||
#define ATOMIC_INTERLOCKED_SUFFIX_1 16
|
||||
#define ATOMIC_INTERLOCKED_SUFFIX_2
|
||||
#define ATOMIC_INTERLOCKED_SUFFIX_3 64
|
||||
|
||||
#define JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size) \
|
||||
typedef struct { \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) repr; \
|
||||
} atomic_##short_type##_t; \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_load_##short_type(const atomic_##short_type##_t *a, \
|
||||
atomic_memory_order_t mo) { \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) ret = a->repr; \
|
||||
if (mo != atomic_memory_order_relaxed) { \
|
||||
atomic_fence(atomic_memory_order_acquire); \
|
||||
} \
|
||||
return (type) ret; \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE void \
|
||||
atomic_store_##short_type(atomic_##short_type##_t *a, \
|
||||
type val, atomic_memory_order_t mo) { \
|
||||
if (mo != atomic_memory_order_relaxed) { \
|
||||
atomic_fence(atomic_memory_order_release); \
|
||||
} \
|
||||
a->repr = (ATOMIC_INTERLOCKED_REPR(lg_size)) val; \
|
||||
if (mo == atomic_memory_order_seq_cst) { \
|
||||
atomic_fence(atomic_memory_order_seq_cst); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchange, \
|
||||
lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) e = \
|
||||
(ATOMIC_INTERLOCKED_REPR(lg_size))*expected; \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) d = \
|
||||
(ATOMIC_INTERLOCKED_REPR(lg_size))desired; \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) old = \
|
||||
ATOMIC_INTERLOCKED_NAME(_InterlockedCompareExchange, \
|
||||
lg_size)(&a->repr, d, e); \
|
||||
if (old == e) { \
|
||||
return true; \
|
||||
} else { \
|
||||
*expected = (type)old; \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
/* We implement the weak version with strong semantics. */ \
|
||||
return atomic_compare_exchange_weak_##short_type(a, expected, \
|
||||
desired, success_mo, failure_mo); \
|
||||
}
|
||||
|
||||
#define JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size) \
|
||||
typedef struct { \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) repr; \
|
||||
} atomic_##short_type##_t; \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_load_##short_type( \
|
||||
const atomic_##short_type##_t *a, atomic_memory_order_t mo) { \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) ret = a->repr; \
|
||||
if (mo != atomic_memory_order_relaxed) { \
|
||||
atomic_fence(atomic_memory_order_acquire); \
|
||||
} \
|
||||
return (type)ret; \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE void atomic_store_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
if (mo != atomic_memory_order_relaxed) { \
|
||||
atomic_fence(atomic_memory_order_release); \
|
||||
} \
|
||||
a->repr = (ATOMIC_INTERLOCKED_REPR(lg_size))val; \
|
||||
if (mo == atomic_memory_order_seq_cst) { \
|
||||
atomic_fence(atomic_memory_order_seq_cst); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type atomic_exchange_##short_type( \
|
||||
atomic_##short_type##_t *a, type val, atomic_memory_order_t mo) { \
|
||||
return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchange, \
|
||||
lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool atomic_compare_exchange_weak_##short_type( \
|
||||
atomic_##short_type##_t *a, type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) \
|
||||
e = (ATOMIC_INTERLOCKED_REPR(lg_size)) * expected; \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) \
|
||||
d = (ATOMIC_INTERLOCKED_REPR(lg_size))desired; \
|
||||
ATOMIC_INTERLOCKED_REPR(lg_size) \
|
||||
old = ATOMIC_INTERLOCKED_NAME( \
|
||||
_InterlockedCompareExchange, lg_size)(&a->repr, d, e); \
|
||||
if (old == e) { \
|
||||
return true; \
|
||||
} else { \
|
||||
*expected = (type)old; \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE bool atomic_compare_exchange_strong_##short_type( \
|
||||
atomic_##short_type##_t *a, type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
/* We implement the weak version with strong semantics. */ \
|
||||
return atomic_compare_exchange_weak_##short_type( \
|
||||
a, expected, desired, success_mo, failure_mo); \
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, lg_size) \
|
||||
JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size) \
|
||||
\
|
||||
|
|
@ -154,5 +156,8 @@ atomic_fetch_xor_##short_type(atomic_##short_type##_t *a, \
|
|||
return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedXor, lg_size)( \
|
||||
&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
|
||||
}
|
||||
/* clang-format on */
|
||||
|
||||
#undef ATOMIC_INLINE
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ATOMIC_MSVC_H */
|
||||
|
|
|
|||
|
|
@ -1,26 +1,31 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H
|
||||
|
||||
extern bool opt_background_thread;
|
||||
extern size_t opt_max_background_threads;
|
||||
extern malloc_mutex_t background_thread_lock;
|
||||
extern atomic_b_t background_thread_enabled_state;
|
||||
extern size_t n_background_threads;
|
||||
extern size_t max_background_threads;
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/background_thread_structs.h"
|
||||
#include "jemalloc/internal/base.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
|
||||
extern bool opt_background_thread;
|
||||
extern size_t opt_max_background_threads;
|
||||
extern malloc_mutex_t background_thread_lock;
|
||||
extern atomic_b_t background_thread_enabled_state;
|
||||
extern size_t n_background_threads;
|
||||
extern size_t max_background_threads;
|
||||
extern background_thread_info_t *background_thread_info;
|
||||
extern bool can_enable_background_thread;
|
||||
|
||||
bool background_thread_create(tsd_t *tsd, unsigned arena_ind);
|
||||
bool background_threads_enable(tsd_t *tsd);
|
||||
bool background_threads_disable(tsd_t *tsd);
|
||||
void background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,
|
||||
arena_decay_t *decay, size_t npages_new);
|
||||
bool background_thread_is_started(background_thread_info_t *info);
|
||||
void background_thread_wakeup_early(
|
||||
background_thread_info_t *info, nstime_t *remaining_sleep);
|
||||
void background_thread_prefork0(tsdn_t *tsdn);
|
||||
void background_thread_prefork1(tsdn_t *tsdn);
|
||||
void background_thread_postfork_parent(tsdn_t *tsdn);
|
||||
void background_thread_postfork_child(tsdn_t *tsdn);
|
||||
bool background_thread_stats_read(tsdn_t *tsdn,
|
||||
background_thread_stats_t *stats);
|
||||
bool background_thread_stats_read(
|
||||
tsdn_t *tsdn, background_thread_stats_t *stats);
|
||||
void background_thread_ctl_init(tsdn_t *tsdn);
|
||||
|
||||
#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER
|
||||
|
|
@ -28,6 +33,6 @@ extern int pthread_create_wrapper(pthread_t *__restrict, const pthread_attr_t *,
|
|||
void *(*)(void *), void *__restrict);
|
||||
#endif
|
||||
bool background_thread_boot0(void);
|
||||
bool background_thread_boot1(tsdn_t *tsdn);
|
||||
bool background_thread_boot1(tsdn_t *tsdn, base_t *base);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H */
|
||||
|
|
|
|||
|
|
@ -1,34 +1,49 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H
|
||||
#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_inlines_a.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/background_thread_externs.h"
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
background_thread_enabled(void) {
|
||||
return atomic_load_b(&background_thread_enabled_state, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
background_thread_enabled_set_impl(bool state) {
|
||||
atomic_store_b(&background_thread_enabled_state, state, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
background_thread_enabled_set(tsdn_t *tsdn, bool state) {
|
||||
malloc_mutex_assert_owner(tsdn, &background_thread_lock);
|
||||
atomic_store_b(&background_thread_enabled_state, state, ATOMIC_RELAXED);
|
||||
background_thread_enabled_set_impl(state);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE background_thread_info_t *
|
||||
arena_background_thread_info_get(arena_t *arena) {
|
||||
unsigned arena_ind = arena_ind_get(arena);
|
||||
return &background_thread_info[arena_ind % ncpus];
|
||||
return &background_thread_info[arena_ind % max_background_threads];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE background_thread_info_t *
|
||||
background_thread_info_get(size_t ind) {
|
||||
return &background_thread_info[ind % max_background_threads];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE uint64_t
|
||||
background_thread_wakeup_time_get(background_thread_info_t *info) {
|
||||
uint64_t next_wakeup = nstime_ns(&info->next_wakeup);
|
||||
assert(atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE) ==
|
||||
(next_wakeup == BACKGROUND_THREAD_INDEFINITE_SLEEP));
|
||||
assert(atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE)
|
||||
== (next_wakeup == BACKGROUND_THREAD_INDEFINITE_SLEEP));
|
||||
return next_wakeup;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
background_thread_wakeup_time_set(tsdn_t *tsdn, background_thread_info_t *info,
|
||||
uint64_t wakeup_time) {
|
||||
background_thread_wakeup_time_set(
|
||||
tsdn_t *tsdn, background_thread_info_t *info, uint64_t wakeup_time) {
|
||||
malloc_mutex_assert_owner(tsdn, &info->mtx);
|
||||
atomic_store_b(&info->indefinite_sleep,
|
||||
wakeup_time == BACKGROUND_THREAD_INDEFINITE_SLEEP, ATOMIC_RELEASE);
|
||||
|
|
@ -40,18 +55,4 @@ background_thread_indefinite_sleep(background_thread_info_t *info) {
|
|||
return atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_background_thread_inactivity_check(tsdn_t *tsdn, arena_t *arena,
|
||||
bool is_background_thread) {
|
||||
if (!background_thread_enabled() || is_background_thread) {
|
||||
return;
|
||||
}
|
||||
background_thread_info_t *info =
|
||||
arena_background_thread_info_get(arena);
|
||||
if (background_thread_indefinite_sleep(info)) {
|
||||
background_thread_interval_check(tsdn, arena,
|
||||
&arena->decay_dirty, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H */
|
||||
|
|
|
|||
|
|
@ -1,14 +1,29 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H
|
||||
#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
|
||||
/* This file really combines "structs" and "types", but only transitionally. */
|
||||
|
||||
#if defined(JEMALLOC_BACKGROUND_THREAD) || defined(JEMALLOC_LAZY_LOCK)
|
||||
# define JEMALLOC_PTHREAD_CREATE_WRAPPER
|
||||
# define JEMALLOC_PTHREAD_CREATE_WRAPPER
|
||||
#endif
|
||||
|
||||
#define BACKGROUND_THREAD_INDEFINITE_SLEEP UINT64_MAX
|
||||
#define MAX_BACKGROUND_THREAD_LIMIT MALLOCX_ARENA_LIMIT
|
||||
#define DEFAULT_NUM_BACKGROUND_THREAD 4
|
||||
|
||||
/*
|
||||
* These exist only as a transitional state. Eventually, deferral should be
|
||||
* part of the PAI, and each implementation can indicate wait times with more
|
||||
* specificity.
|
||||
*/
|
||||
#define BACKGROUND_THREAD_HPA_INTERVAL_MAX_UNINITIALIZED (-2)
|
||||
#define BACKGROUND_THREAD_HPA_INTERVAL_MAX_DEFAULT_WHEN_ENABLED 5000
|
||||
|
||||
#define BACKGROUND_THREAD_DEFERRED_MIN UINT64_C(0)
|
||||
#define BACKGROUND_THREAD_DEFERRED_MAX UINT64_MAX
|
||||
|
||||
typedef enum {
|
||||
background_thread_stopped,
|
||||
|
|
@ -20,33 +35,34 @@ typedef enum {
|
|||
struct background_thread_info_s {
|
||||
#ifdef JEMALLOC_BACKGROUND_THREAD
|
||||
/* Background thread is pthread specific. */
|
||||
pthread_t thread;
|
||||
pthread_cond_t cond;
|
||||
pthread_t thread;
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
malloc_mutex_t mtx;
|
||||
background_thread_state_t state;
|
||||
malloc_mutex_t mtx;
|
||||
background_thread_state_t state;
|
||||
/* When true, it means no wakeup scheduled. */
|
||||
atomic_b_t indefinite_sleep;
|
||||
atomic_b_t indefinite_sleep;
|
||||
/* Next scheduled wakeup time (absolute time in ns). */
|
||||
nstime_t next_wakeup;
|
||||
nstime_t next_wakeup;
|
||||
/*
|
||||
* Since the last background thread run, newly added number of pages
|
||||
* that need to be purged by the next wakeup. This is adjusted on
|
||||
* epoch advance, and is used to determine whether we should signal the
|
||||
* background thread to wake up earlier.
|
||||
*/
|
||||
size_t npages_to_purge_new;
|
||||
size_t npages_to_purge_new;
|
||||
/* Stats: total number of runs since started. */
|
||||
uint64_t tot_n_runs;
|
||||
uint64_t tot_n_runs;
|
||||
/* Stats: total sleep time since started. */
|
||||
nstime_t tot_sleep_time;
|
||||
nstime_t tot_sleep_time;
|
||||
};
|
||||
typedef struct background_thread_info_s background_thread_info_t;
|
||||
|
||||
struct background_thread_stats_s {
|
||||
size_t num_threads;
|
||||
uint64_t num_runs;
|
||||
nstime_t run_interval;
|
||||
size_t num_threads;
|
||||
uint64_t num_runs;
|
||||
nstime_t run_interval;
|
||||
mutex_prof_data_t max_counter_per_bg_thd;
|
||||
};
|
||||
typedef struct background_thread_stats_s background_thread_stats_t;
|
||||
|
||||
|
|
|
|||
125
include/jemalloc/internal/base.h
Normal file
125
include/jemalloc/internal/base.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BASE_H
|
||||
#define JEMALLOC_INTERNAL_BASE_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/edata.h"
|
||||
#include "jemalloc/internal/ehooks.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
|
||||
/*
|
||||
* Alignment when THP is not enabled. Set to constant 2M in case the HUGEPAGE
|
||||
* value is unexpected high (which would cause VM over-reservation).
|
||||
*/
|
||||
#define BASE_BLOCK_MIN_ALIGN ((size_t)2 << 20)
|
||||
|
||||
enum metadata_thp_mode_e {
|
||||
metadata_thp_disabled = 0,
|
||||
/*
|
||||
* Lazily enable hugepage for metadata. To avoid high RSS caused by THP
|
||||
* + low usage arena (i.e. THP becomes a significant percentage), the
|
||||
* "auto" option only starts using THP after a base allocator used up
|
||||
* the first THP region. Starting from the second hugepage (in a single
|
||||
* arena), "auto" behaves the same as "always", i.e. madvise hugepage
|
||||
* right away.
|
||||
*/
|
||||
metadata_thp_auto = 1,
|
||||
metadata_thp_always = 2,
|
||||
metadata_thp_mode_limit = 3
|
||||
};
|
||||
typedef enum metadata_thp_mode_e metadata_thp_mode_t;
|
||||
|
||||
#define METADATA_THP_DEFAULT metadata_thp_disabled
|
||||
extern metadata_thp_mode_t opt_metadata_thp;
|
||||
extern const char *const metadata_thp_mode_names[];
|
||||
|
||||
/* Embedded at the beginning of every block of base-managed virtual memory. */
|
||||
typedef struct base_block_s base_block_t;
|
||||
struct base_block_s {
|
||||
/* Total size of block's virtual memory mapping. */
|
||||
size_t size;
|
||||
|
||||
/* Next block in list of base's blocks. */
|
||||
base_block_t *next;
|
||||
|
||||
/* Tracks unused trailing space. */
|
||||
edata_t edata;
|
||||
};
|
||||
|
||||
typedef struct base_s base_t;
|
||||
struct base_s {
|
||||
/*
|
||||
* User-configurable extent hook functions.
|
||||
*/
|
||||
ehooks_t ehooks;
|
||||
|
||||
/*
|
||||
* User-configurable extent hook functions for metadata allocations.
|
||||
*/
|
||||
ehooks_t ehooks_base;
|
||||
|
||||
/* Protects base_alloc() and base_stats_get() operations. */
|
||||
malloc_mutex_t mtx;
|
||||
|
||||
/* Using THP when true (metadata_thp auto mode). */
|
||||
bool auto_thp_switched;
|
||||
/*
|
||||
* Most recent size class in the series of increasingly large base
|
||||
* extents. Logarithmic spacing between subsequent allocations ensures
|
||||
* that the total number of distinct mappings remains small.
|
||||
*/
|
||||
pszind_t pind_last;
|
||||
|
||||
/* Serial number generation state. */
|
||||
size_t extent_sn_next;
|
||||
|
||||
/* Chain of all blocks associated with base. */
|
||||
base_block_t *blocks;
|
||||
|
||||
/* Heap of extents that track unused trailing space within blocks. */
|
||||
edata_heap_t avail[SC_NSIZES];
|
||||
|
||||
/* Contains reusable base edata (used by tcache_stacks currently). */
|
||||
edata_avail_t edata_avail;
|
||||
|
||||
/* Stats, only maintained if config_stats. */
|
||||
size_t allocated;
|
||||
size_t edata_allocated;
|
||||
size_t rtree_allocated;
|
||||
size_t resident;
|
||||
size_t mapped;
|
||||
/* Number of THP regions touched. */
|
||||
size_t n_thp;
|
||||
};
|
||||
|
||||
static inline unsigned
|
||||
base_ind_get(const base_t *base) {
|
||||
return ehooks_ind_get(&base->ehooks);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
metadata_thp_enabled(void) {
|
||||
return (opt_metadata_thp != metadata_thp_disabled);
|
||||
}
|
||||
|
||||
base_t *b0get(void);
|
||||
base_t *base_new(tsdn_t *tsdn, unsigned ind, const extent_hooks_t *extent_hooks,
|
||||
bool metadata_use_hooks);
|
||||
void base_delete(tsdn_t *tsdn, base_t *base);
|
||||
ehooks_t *base_ehooks_get(base_t *base);
|
||||
ehooks_t *base_ehooks_get_for_metadata(base_t *base);
|
||||
extent_hooks_t *base_extent_hooks_set(
|
||||
base_t *base, extent_hooks_t *extent_hooks);
|
||||
void *base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment);
|
||||
edata_t *base_alloc_edata(tsdn_t *tsdn, base_t *base);
|
||||
void *base_alloc_rtree(tsdn_t *tsdn, base_t *base, size_t size);
|
||||
void *b0_alloc_tcache_stack(tsdn_t *tsdn, size_t size);
|
||||
void b0_dalloc_tcache_stack(tsdn_t *tsdn, void *tcache_stack);
|
||||
void base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated,
|
||||
size_t *edata_allocated, size_t *rtree_allocated, size_t *resident,
|
||||
size_t *mapped, size_t *n_thp);
|
||||
void base_prefork(tsdn_t *tsdn, base_t *base);
|
||||
void base_postfork_parent(tsdn_t *tsdn, base_t *base);
|
||||
void base_postfork_child(tsdn_t *tsdn, base_t *base);
|
||||
bool base_boot(tsdn_t *tsdn);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BASE_H */
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BASE_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_BASE_EXTERNS_H
|
||||
|
||||
extern metadata_thp_mode_t opt_metadata_thp;
|
||||
extern const char *metadata_thp_mode_names[];
|
||||
|
||||
base_t *b0get(void);
|
||||
base_t *base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
|
||||
void base_delete(tsdn_t *tsdn, base_t *base);
|
||||
extent_hooks_t *base_extent_hooks_get(base_t *base);
|
||||
extent_hooks_t *base_extent_hooks_set(base_t *base,
|
||||
extent_hooks_t *extent_hooks);
|
||||
void *base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment);
|
||||
extent_t *base_alloc_extent(tsdn_t *tsdn, base_t *base);
|
||||
void base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated,
|
||||
size_t *resident, size_t *mapped, size_t *n_thp);
|
||||
void base_prefork(tsdn_t *tsdn, base_t *base);
|
||||
void base_postfork_parent(tsdn_t *tsdn, base_t *base);
|
||||
void base_postfork_child(tsdn_t *tsdn, base_t *base);
|
||||
bool base_boot(tsdn_t *tsdn);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BASE_EXTERNS_H */
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BASE_INLINES_H
|
||||
#define JEMALLOC_INTERNAL_BASE_INLINES_H
|
||||
|
||||
static inline unsigned
|
||||
base_ind_get(const base_t *base) {
|
||||
return base->ind;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
metadata_thp_enabled(void) {
|
||||
return (opt_metadata_thp != metadata_thp_disabled);
|
||||
}
|
||||
#endif /* JEMALLOC_INTERNAL_BASE_INLINES_H */
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BASE_STRUCTS_H
|
||||
#define JEMALLOC_INTERNAL_BASE_STRUCTS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
|
||||
/* Embedded at the beginning of every block of base-managed virtual memory. */
|
||||
struct base_block_s {
|
||||
/* Total size of block's virtual memory mapping. */
|
||||
size_t size;
|
||||
|
||||
/* Next block in list of base's blocks. */
|
||||
base_block_t *next;
|
||||
|
||||
/* Tracks unused trailing space. */
|
||||
extent_t extent;
|
||||
};
|
||||
|
||||
struct base_s {
|
||||
/* Associated arena's index within the arenas array. */
|
||||
unsigned ind;
|
||||
|
||||
/*
|
||||
* User-configurable extent hook functions. Points to an
|
||||
* extent_hooks_t.
|
||||
*/
|
||||
atomic_p_t extent_hooks;
|
||||
|
||||
/* Protects base_alloc() and base_stats_get() operations. */
|
||||
malloc_mutex_t mtx;
|
||||
|
||||
/* Using THP when true (metadata_thp auto mode). */
|
||||
bool auto_thp_switched;
|
||||
/*
|
||||
* Most recent size class in the series of increasingly large base
|
||||
* extents. Logarithmic spacing between subsequent allocations ensures
|
||||
* that the total number of distinct mappings remains small.
|
||||
*/
|
||||
pszind_t pind_last;
|
||||
|
||||
/* Serial number generation state. */
|
||||
size_t extent_sn_next;
|
||||
|
||||
/* Chain of all blocks associated with base. */
|
||||
base_block_t *blocks;
|
||||
|
||||
/* Heap of extents that track unused trailing space within blocks. */
|
||||
extent_heap_t avail[NSIZES];
|
||||
|
||||
/* Stats, only maintained if config_stats. */
|
||||
size_t allocated;
|
||||
size_t resident;
|
||||
size_t mapped;
|
||||
/* Number of THP regions touched. */
|
||||
size_t n_thp;
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BASE_STRUCTS_H */
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BASE_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_BASE_TYPES_H
|
||||
|
||||
typedef struct base_block_s base_block_t;
|
||||
typedef struct base_s base_t;
|
||||
|
||||
#define METADATA_THP_DEFAULT metadata_thp_disabled
|
||||
|
||||
/*
|
||||
* In auto mode, arenas switch to huge pages for the base allocator on the
|
||||
* second base block. a0 switches to thp on the 5th block (after 20 megabytes
|
||||
* of metadata), since more metadata (e.g. rtree nodes) come from a0's base.
|
||||
*/
|
||||
|
||||
#define BASE_AUTO_THP_THRESHOLD 2
|
||||
#define BASE_AUTO_THP_THRESHOLD_A0 5
|
||||
|
||||
typedef enum {
|
||||
metadata_thp_disabled = 0,
|
||||
/*
|
||||
* Lazily enable hugepage for metadata. To avoid high RSS caused by THP
|
||||
* + low usage arena (i.e. THP becomes a significant percentage), the
|
||||
* "auto" option only starts using THP after a base allocator used up
|
||||
* the first THP region. Starting from the second hugepage (in a single
|
||||
* arena), "auto" behaves the same as "always", i.e. madvise hugepage
|
||||
* right away.
|
||||
*/
|
||||
metadata_thp_auto = 1,
|
||||
metadata_thp_always = 2,
|
||||
metadata_thp_mode_limit = 3
|
||||
} metadata_thp_mode_t;
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BASE_TYPES_H */
|
||||
|
|
@ -1,60 +1,28 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BIN_H
|
||||
#define JEMALLOC_INTERNAL_BIN_H
|
||||
|
||||
#include "jemalloc/internal/extent_types.h"
|
||||
#include "jemalloc/internal/extent_structs.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/bin_info.h"
|
||||
#include "jemalloc/internal/bin_stats.h"
|
||||
#include "jemalloc/internal/bin_types.h"
|
||||
#include "jemalloc/internal/edata.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/*
|
||||
* A bin contains a set of extents that are currently being used for slab
|
||||
* allocations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Read-only information associated with each element of arena_t's bins array
|
||||
* is stored separately, partly to reduce memory usage (only one copy, rather
|
||||
* than one per arena), but mainly to avoid false cacheline sharing.
|
||||
*
|
||||
* Each slab has the following layout:
|
||||
*
|
||||
* /--------------------\
|
||||
* | region 0 |
|
||||
* |--------------------|
|
||||
* | region 1 |
|
||||
* |--------------------|
|
||||
* | ... |
|
||||
* | ... |
|
||||
* | ... |
|
||||
* |--------------------|
|
||||
* | region nregs-1 |
|
||||
* \--------------------/
|
||||
*/
|
||||
typedef struct bin_info_s bin_info_t;
|
||||
struct bin_info_s {
|
||||
/* Size of regions in a slab for this bin's size class. */
|
||||
size_t reg_size;
|
||||
|
||||
/* Total size of a slab for this bin's size class. */
|
||||
size_t slab_size;
|
||||
|
||||
/* Total number of regions in a slab for this bin's size class. */
|
||||
uint32_t nregs;
|
||||
|
||||
/*
|
||||
* Metadata used to manipulate bitmaps for slabs associated with this
|
||||
* bin.
|
||||
*/
|
||||
bitmap_info_t bitmap_info;
|
||||
};
|
||||
|
||||
extern const bin_info_t bin_infos[NBINS];
|
||||
|
||||
|
||||
typedef struct bin_s bin_t;
|
||||
struct bin_s {
|
||||
/* All operations on bin_t fields require lock ownership. */
|
||||
malloc_mutex_t lock;
|
||||
malloc_mutex_t lock;
|
||||
|
||||
/*
|
||||
* Bin statistics. These get touched every time the lock is acquired,
|
||||
* so put them close by in the hopes of getting some cache locality.
|
||||
*/
|
||||
bin_stats_t stats;
|
||||
|
||||
/*
|
||||
* Current slab being used to service allocations of this bin's size
|
||||
|
|
@ -62,22 +30,30 @@ struct bin_s {
|
|||
* slabcur is reassigned, the previous slab must be deallocated or
|
||||
* inserted into slabs_{nonfull,full}.
|
||||
*/
|
||||
extent_t *slabcur;
|
||||
edata_t *slabcur;
|
||||
|
||||
/*
|
||||
* Heap of non-full slabs. This heap is used to assure that new
|
||||
* allocations come from the non-full slab that is oldest/lowest in
|
||||
* memory.
|
||||
*/
|
||||
extent_heap_t slabs_nonfull;
|
||||
edata_heap_t slabs_nonfull;
|
||||
|
||||
/* List used to track full slabs. */
|
||||
extent_list_t slabs_full;
|
||||
|
||||
/* Bin statistics. */
|
||||
bin_stats_t stats;
|
||||
edata_list_active_t slabs_full;
|
||||
};
|
||||
|
||||
/* A set of sharded bins of the same size class. */
|
||||
typedef struct bins_s bins_t;
|
||||
struct bins_s {
|
||||
/* Sharded bins. Dynamically sized. */
|
||||
bin_t *bin_shards;
|
||||
};
|
||||
|
||||
void bin_shard_sizes_boot(unsigned bin_shard_sizes[SC_NBINS]);
|
||||
bool bin_update_shard_size(unsigned bin_shards[SC_NBINS], size_t start_size,
|
||||
size_t end_size, size_t nshards);
|
||||
|
||||
/* Initializes a bin to empty. Returns true on error. */
|
||||
bool bin_init(bin_t *bin);
|
||||
|
||||
|
|
@ -86,20 +62,59 @@ void bin_prefork(tsdn_t *tsdn, bin_t *bin);
|
|||
void bin_postfork_parent(tsdn_t *tsdn, bin_t *bin);
|
||||
void bin_postfork_child(tsdn_t *tsdn, bin_t *bin);
|
||||
|
||||
/* Slab region allocation. */
|
||||
void *bin_slab_reg_alloc(edata_t *slab, const bin_info_t *bin_info);
|
||||
void bin_slab_reg_alloc_batch(
|
||||
edata_t *slab, const bin_info_t *bin_info, unsigned cnt, void **ptrs);
|
||||
|
||||
/* Slab list management. */
|
||||
void bin_slabs_nonfull_insert(bin_t *bin, edata_t *slab);
|
||||
void bin_slabs_nonfull_remove(bin_t *bin, edata_t *slab);
|
||||
edata_t *bin_slabs_nonfull_tryget(bin_t *bin);
|
||||
void bin_slabs_full_insert(bool is_auto, bin_t *bin, edata_t *slab);
|
||||
void bin_slabs_full_remove(bool is_auto, bin_t *bin, edata_t *slab);
|
||||
|
||||
/* Slab association / demotion. */
|
||||
void bin_dissociate_slab(bool is_auto, edata_t *slab, bin_t *bin);
|
||||
void bin_lower_slab(tsdn_t *tsdn, bool is_auto, edata_t *slab, bin_t *bin);
|
||||
|
||||
/* Deallocation helpers (called under bin lock). */
|
||||
void bin_dalloc_slab_prepare(tsdn_t *tsdn, edata_t *slab, bin_t *bin);
|
||||
void bin_dalloc_locked_handle_newly_empty(
|
||||
tsdn_t *tsdn, bool is_auto, edata_t *slab, bin_t *bin);
|
||||
void bin_dalloc_locked_handle_newly_nonempty(
|
||||
tsdn_t *tsdn, bool is_auto, edata_t *slab, bin_t *bin);
|
||||
|
||||
/* Slabcur refill and allocation. */
|
||||
void bin_refill_slabcur_with_fresh_slab(tsdn_t *tsdn, bin_t *bin,
|
||||
szind_t binind, edata_t *fresh_slab);
|
||||
void *bin_malloc_with_fresh_slab(tsdn_t *tsdn, bin_t *bin,
|
||||
szind_t binind, edata_t *fresh_slab);
|
||||
bool bin_refill_slabcur_no_fresh_slab(tsdn_t *tsdn, bool is_auto,
|
||||
bin_t *bin);
|
||||
void *bin_malloc_no_fresh_slab(tsdn_t *tsdn, bool is_auto, bin_t *bin,
|
||||
szind_t binind);
|
||||
|
||||
/* Bin selection. */
|
||||
bin_t *bin_choose(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
unsigned *binshard_p);
|
||||
|
||||
/* Stats. */
|
||||
static inline void
|
||||
bin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {
|
||||
bin_stats_merge(tsdn_t *tsdn, bin_stats_data_t *dst_bin_stats, bin_t *bin) {
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
malloc_mutex_prof_read(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
|
||||
dst_bin_stats->nmalloc += bin->stats.nmalloc;
|
||||
dst_bin_stats->ndalloc += bin->stats.ndalloc;
|
||||
dst_bin_stats->nrequests += bin->stats.nrequests;
|
||||
dst_bin_stats->curregs += bin->stats.curregs;
|
||||
dst_bin_stats->nfills += bin->stats.nfills;
|
||||
dst_bin_stats->nflushes += bin->stats.nflushes;
|
||||
dst_bin_stats->nslabs += bin->stats.nslabs;
|
||||
dst_bin_stats->reslabs += bin->stats.reslabs;
|
||||
dst_bin_stats->curslabs += bin->stats.curslabs;
|
||||
malloc_mutex_prof_accum(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
|
||||
bin_stats_t *stats = &dst_bin_stats->stats_data;
|
||||
stats->nmalloc += bin->stats.nmalloc;
|
||||
stats->ndalloc += bin->stats.ndalloc;
|
||||
stats->nrequests += bin->stats.nrequests;
|
||||
stats->curregs += bin->stats.curregs;
|
||||
stats->nfills += bin->stats.nfills;
|
||||
stats->nflushes += bin->stats.nflushes;
|
||||
stats->nslabs += bin->stats.nslabs;
|
||||
stats->reslabs += bin->stats.reslabs;
|
||||
stats->curslabs += bin->stats.curslabs;
|
||||
stats->nonfull_slabs += bin->stats.nonfull_slabs;
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
51
include/jemalloc/internal/bin_info.h
Normal file
51
include/jemalloc/internal/bin_info.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BIN_INFO_H
|
||||
#define JEMALLOC_INTERNAL_BIN_INFO_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
|
||||
/*
|
||||
* Read-only information associated with each element of arena_t's bins array
|
||||
* is stored separately, partly to reduce memory usage (only one copy, rather
|
||||
* than one per arena), but mainly to avoid false cacheline sharing.
|
||||
*
|
||||
* Each slab has the following layout:
|
||||
*
|
||||
* /--------------------\
|
||||
* | region 0 |
|
||||
* |--------------------|
|
||||
* | region 1 |
|
||||
* |--------------------|
|
||||
* | ... |
|
||||
* | ... |
|
||||
* | ... |
|
||||
* |--------------------|
|
||||
* | region nregs-1 |
|
||||
* \--------------------/
|
||||
*/
|
||||
typedef struct bin_info_s bin_info_t;
|
||||
struct bin_info_s {
|
||||
/* Size of regions in a slab for this bin's size class. */
|
||||
size_t reg_size;
|
||||
|
||||
/* Total size of a slab for this bin's size class. */
|
||||
size_t slab_size;
|
||||
|
||||
/* Total number of regions in a slab for this bin's size class. */
|
||||
uint32_t nregs;
|
||||
|
||||
/* Number of sharded bins in each arena for this size class. */
|
||||
uint32_t n_shards;
|
||||
|
||||
/*
|
||||
* Metadata used to manipulate bitmaps for slabs associated with this
|
||||
* bin.
|
||||
*/
|
||||
bitmap_info_t bitmap_info;
|
||||
};
|
||||
|
||||
extern bin_info_t bin_infos[SC_NBINS];
|
||||
|
||||
void bin_info_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BIN_INFO_H */
|
||||
112
include/jemalloc/internal/bin_inlines.h
Normal file
112
include/jemalloc/internal/bin_inlines.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BIN_INLINES_H
|
||||
#define JEMALLOC_INTERNAL_BIN_INLINES_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/bin.h"
|
||||
#include "jemalloc/internal/bin_info.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
#include "jemalloc/internal/div.h"
|
||||
#include "jemalloc/internal/edata.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/*
|
||||
* The dalloc bin info contains just the information that the common paths need
|
||||
* during tcache flushes. By force-inlining these paths, and using local copies
|
||||
* of data (so that the compiler knows it's constant), we avoid a whole bunch of
|
||||
* redundant loads and stores by leaving this information in registers.
|
||||
*/
|
||||
typedef struct bin_dalloc_locked_info_s bin_dalloc_locked_info_t;
|
||||
struct bin_dalloc_locked_info_s {
|
||||
div_info_t div_info;
|
||||
uint32_t nregs;
|
||||
uint64_t ndalloc;
|
||||
};
|
||||
|
||||
/* Find the region index of a pointer within a slab. */
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
bin_slab_regind_impl(
|
||||
div_info_t *div_info, szind_t binind, edata_t *slab, const void *ptr) {
|
||||
size_t diff, regind;
|
||||
|
||||
/* Freeing a pointer outside the slab can cause assertion failure. */
|
||||
assert((uintptr_t)ptr >= (uintptr_t)edata_addr_get(slab));
|
||||
assert((uintptr_t)ptr < (uintptr_t)edata_past_get(slab));
|
||||
/* Freeing an interior pointer can cause assertion failure. */
|
||||
assert(((uintptr_t)ptr - (uintptr_t)edata_addr_get(slab))
|
||||
% (uintptr_t)bin_infos[binind].reg_size
|
||||
== 0);
|
||||
|
||||
diff = (size_t)((uintptr_t)ptr - (uintptr_t)edata_addr_get(slab));
|
||||
|
||||
/* Avoid doing division with a variable divisor. */
|
||||
regind = div_compute(div_info, diff);
|
||||
assert(regind < bin_infos[binind].nregs);
|
||||
return regind;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
bin_slab_regind(bin_dalloc_locked_info_t *info, szind_t binind,
|
||||
edata_t *slab, const void *ptr) {
|
||||
size_t regind = bin_slab_regind_impl(
|
||||
&info->div_info, binind, slab, ptr);
|
||||
return regind;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
bin_dalloc_locked_begin(
|
||||
bin_dalloc_locked_info_t *info, szind_t binind) {
|
||||
info->div_info = arena_binind_div_info[binind];
|
||||
info->nregs = bin_infos[binind].nregs;
|
||||
info->ndalloc = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does the deallocation work associated with freeing a single pointer (a
|
||||
* "step") in between a bin_dalloc_locked begin and end call.
|
||||
*
|
||||
* Returns true if arena_slab_dalloc must be called on slab. Doesn't do
|
||||
* stats updates, which happen during finish (this lets running counts get left
|
||||
* in a register).
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
bin_dalloc_locked_step(tsdn_t *tsdn, bool is_auto, bin_t *bin,
|
||||
bin_dalloc_locked_info_t *info, szind_t binind, edata_t *slab,
|
||||
void *ptr) {
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
size_t regind = bin_slab_regind(info, binind, slab, ptr);
|
||||
slab_data_t *slab_data = edata_slab_data_get(slab);
|
||||
|
||||
assert(edata_nfree_get(slab) < bin_info->nregs);
|
||||
/* Freeing an unallocated pointer can cause assertion failure. */
|
||||
assert(bitmap_get(slab_data->bitmap, &bin_info->bitmap_info, regind));
|
||||
|
||||
bitmap_unset(slab_data->bitmap, &bin_info->bitmap_info, regind);
|
||||
edata_nfree_inc(slab);
|
||||
|
||||
if (config_stats) {
|
||||
info->ndalloc++;
|
||||
}
|
||||
|
||||
unsigned nfree = edata_nfree_get(slab);
|
||||
if (nfree == bin_info->nregs) {
|
||||
bin_dalloc_locked_handle_newly_empty(
|
||||
tsdn, is_auto, slab, bin);
|
||||
return true;
|
||||
} else if (nfree == 1 && slab != bin->slabcur) {
|
||||
bin_dalloc_locked_handle_newly_nonempty(
|
||||
tsdn, is_auto, slab, bin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
bin_dalloc_locked_finish(tsdn_t *tsdn, bin_t *bin,
|
||||
bin_dalloc_locked_info_t *info) {
|
||||
if (config_stats) {
|
||||
bin->stats.ndalloc += info->ndalloc;
|
||||
assert(bin->stats.curregs >= (size_t)info->ndalloc);
|
||||
bin->stats.curregs -= (size_t)info->ndalloc;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BIN_INLINES_H */
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BIN_STATS_H
|
||||
#define JEMALLOC_INTERNAL_BIN_STATS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/mutex_prof.h"
|
||||
|
||||
typedef struct bin_stats_s bin_stats_t;
|
||||
|
|
@ -11,41 +12,47 @@ struct bin_stats_s {
|
|||
* many times, resulting many increments to nrequests, but only one
|
||||
* each to nmalloc and ndalloc.
|
||||
*/
|
||||
uint64_t nmalloc;
|
||||
uint64_t ndalloc;
|
||||
uint64_t nmalloc;
|
||||
uint64_t ndalloc;
|
||||
|
||||
/*
|
||||
* Number of allocation requests that correspond to the size of this
|
||||
* bin. This includes requests served by tcache, though tcache only
|
||||
* periodically merges into this counter.
|
||||
*/
|
||||
uint64_t nrequests;
|
||||
uint64_t nrequests;
|
||||
|
||||
/*
|
||||
* Current number of regions of this size class, including regions
|
||||
* currently cached by tcache.
|
||||
*/
|
||||
size_t curregs;
|
||||
size_t curregs;
|
||||
|
||||
/* Number of tcache fills from this bin. */
|
||||
uint64_t nfills;
|
||||
uint64_t nfills;
|
||||
|
||||
/* Number of tcache flushes to this bin. */
|
||||
uint64_t nflushes;
|
||||
uint64_t nflushes;
|
||||
|
||||
/* Total number of slabs created for this bin's size class. */
|
||||
uint64_t nslabs;
|
||||
uint64_t nslabs;
|
||||
|
||||
/*
|
||||
* Total number of slabs reused by extracting them from the slabs heap
|
||||
* for this bin's size class.
|
||||
*/
|
||||
uint64_t reslabs;
|
||||
uint64_t reslabs;
|
||||
|
||||
/* Current number of slabs in this bin. */
|
||||
size_t curslabs;
|
||||
size_t curslabs;
|
||||
|
||||
mutex_prof_data_t mutex_data;
|
||||
/* Current size of nonfull slabs heap in this bin. */
|
||||
size_t nonfull_slabs;
|
||||
};
|
||||
|
||||
typedef struct bin_stats_data_s bin_stats_data_t;
|
||||
struct bin_stats_data_s {
|
||||
bin_stats_t stats_data;
|
||||
mutex_prof_data_t mutex_data;
|
||||
};
|
||||
#endif /* JEMALLOC_INTERNAL_BIN_STATS_H */
|
||||
|
|
|
|||
21
include/jemalloc/internal/bin_types.h
Normal file
21
include/jemalloc/internal/bin_types.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BIN_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_BIN_TYPES_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
#define BIN_SHARDS_MAX (1 << EDATA_BITS_BINSHARD_WIDTH)
|
||||
#define N_BIN_SHARDS_DEFAULT 1
|
||||
|
||||
/* Used in TSD static initializer only. Real init in arena_bind(). */
|
||||
#define TSD_BINSHARDS_ZERO_INITIALIZER \
|
||||
{ \
|
||||
{ UINT8_MAX } \
|
||||
}
|
||||
|
||||
typedef struct tsd_binshards_s tsd_binshards_t;
|
||||
struct tsd_binshards_s {
|
||||
uint8_t binshard[SC_NBINS];
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BIN_TYPES_H */
|
||||
|
|
@ -1,93 +1,391 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BIT_UTIL_H
|
||||
#define JEMALLOC_INTERNAL_BIT_UTIL_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
|
||||
#define BIT_UTIL_INLINE static inline
|
||||
|
||||
/* Sanity check. */
|
||||
#if !defined(JEMALLOC_INTERNAL_FFSLL) || !defined(JEMALLOC_INTERNAL_FFSL) \
|
||||
#if !defined(JEMALLOC_INTERNAL_FFSLL) || !defined(JEMALLOC_INTERNAL_FFSL) \
|
||||
|| !defined(JEMALLOC_INTERNAL_FFS)
|
||||
# error JEMALLOC_INTERNAL_FFS{,L,LL} should have been defined by configure
|
||||
# error JEMALLOC_INTERNAL_FFS{,L,LL} should have been defined by configure
|
||||
#endif
|
||||
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
ffs_llu(unsigned long long bitmap) {
|
||||
return JEMALLOC_INTERNAL_FFSLL(bitmap);
|
||||
/*
|
||||
* Unlike the builtins and posix ffs functions, our ffs requires a non-zero
|
||||
* input, and returns the position of the lowest bit set (as opposed to the
|
||||
* posix versions, which return 1 larger than that position and use a return
|
||||
* value of zero as a sentinel. This tends to simplify logic in callers, and
|
||||
* allows for consistency with the builtins we build fls on top of.
|
||||
*/
|
||||
static inline unsigned
|
||||
ffs_llu(unsigned long long x) {
|
||||
util_assume(x != 0);
|
||||
return JEMALLOC_INTERNAL_FFSLL(x) - 1;
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
ffs_lu(unsigned long bitmap) {
|
||||
return JEMALLOC_INTERNAL_FFSL(bitmap);
|
||||
static inline unsigned
|
||||
ffs_lu(unsigned long x) {
|
||||
util_assume(x != 0);
|
||||
return JEMALLOC_INTERNAL_FFSL(x) - 1;
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
ffs_u(unsigned bitmap) {
|
||||
return JEMALLOC_INTERNAL_FFS(bitmap);
|
||||
static inline unsigned
|
||||
ffs_u(unsigned x) {
|
||||
util_assume(x != 0);
|
||||
return JEMALLOC_INTERNAL_FFS(x) - 1;
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
ffs_zu(size_t bitmap) {
|
||||
/* clang-format off */
|
||||
#define DO_FLS_SLOW(x, suffix) do { \
|
||||
util_assume(x != 0); \
|
||||
x |= (x >> 1); \
|
||||
x |= (x >> 2); \
|
||||
x |= (x >> 4); \
|
||||
x |= (x >> 8); \
|
||||
x |= (x >> 16); \
|
||||
if (sizeof(x) > 4) { \
|
||||
/* \
|
||||
* If sizeof(x) is 4, then the expression "x >> 32" \
|
||||
* will generate compiler warnings even if the code \
|
||||
* never executes. This circumvents the warning, and \
|
||||
* gets compiled out in optimized builds. \
|
||||
*/ \
|
||||
int constant_32 = sizeof(x) * 4; \
|
||||
x |= (x >> constant_32); \
|
||||
} \
|
||||
x++; \
|
||||
if (x == 0) { \
|
||||
return 8 * sizeof(x) - 1; \
|
||||
} \
|
||||
return ffs_##suffix(x) - 1; \
|
||||
} while(0)
|
||||
/* clang-format on */
|
||||
|
||||
static inline unsigned
|
||||
fls_llu_slow(unsigned long long x) {
|
||||
DO_FLS_SLOW(x, llu);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_lu_slow(unsigned long x) {
|
||||
DO_FLS_SLOW(x, lu);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_u_slow(unsigned x) {
|
||||
DO_FLS_SLOW(x, u);
|
||||
}
|
||||
|
||||
#undef DO_FLS_SLOW
|
||||
|
||||
#ifdef JEMALLOC_HAVE_BUILTIN_CLZ
|
||||
static inline unsigned
|
||||
fls_llu(unsigned long long x) {
|
||||
util_assume(x != 0);
|
||||
/*
|
||||
* Note that the xor here is more naturally written as subtraction; the
|
||||
* last bit set is the number of bits in the type minus the number of
|
||||
* leading zero bits. But GCC implements that as:
|
||||
* bsr edi, edi
|
||||
* mov eax, 31
|
||||
* xor edi, 31
|
||||
* sub eax, edi
|
||||
* If we write it as xor instead, then we get
|
||||
* bsr eax, edi
|
||||
* as desired.
|
||||
*/
|
||||
return (8 * sizeof(x) - 1) ^ __builtin_clzll(x);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_lu(unsigned long x) {
|
||||
util_assume(x != 0);
|
||||
return (8 * sizeof(x) - 1) ^ __builtin_clzl(x);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_u(unsigned x) {
|
||||
util_assume(x != 0);
|
||||
return (8 * sizeof(x) - 1) ^ __builtin_clz(x);
|
||||
}
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
# if LG_SIZEOF_PTR == 3
|
||||
# define DO_BSR64(bit, x) _BitScanReverse64(&bit, x)
|
||||
# else
|
||||
/*
|
||||
* This never actually runs; we're just dodging a compiler error for the
|
||||
* never-taken branch where sizeof(void *) == 8.
|
||||
*/
|
||||
# define DO_BSR64(bit, x) \
|
||||
bit = 0; \
|
||||
unreachable()
|
||||
# endif
|
||||
|
||||
/* clang-format off */
|
||||
#define DO_FLS(x) do { \
|
||||
if (x == 0) { \
|
||||
return 8 * sizeof(x); \
|
||||
} \
|
||||
unsigned long bit; \
|
||||
if (sizeof(x) == 4) { \
|
||||
_BitScanReverse(&bit, (unsigned)x); \
|
||||
return (unsigned)bit; \
|
||||
} \
|
||||
if (sizeof(x) == 8 && sizeof(void *) == 8) { \
|
||||
DO_BSR64(bit, x); \
|
||||
return (unsigned)bit; \
|
||||
} \
|
||||
if (sizeof(x) == 8 && sizeof(void *) == 4) { \
|
||||
/* Dodge a compiler warning, as above. */ \
|
||||
int constant_32 = sizeof(x) * 4; \
|
||||
if (_BitScanReverse(&bit, \
|
||||
(unsigned)(x >> constant_32))) { \
|
||||
return 32 + (unsigned)bit; \
|
||||
} else { \
|
||||
_BitScanReverse(&bit, (unsigned)x); \
|
||||
return (unsigned)bit; \
|
||||
} \
|
||||
} \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
/* clang-format on */
|
||||
|
||||
static inline unsigned
|
||||
fls_llu(unsigned long long x) {
|
||||
DO_FLS(x);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_lu(unsigned long x) {
|
||||
DO_FLS(x);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_u(unsigned x) {
|
||||
DO_FLS(x);
|
||||
}
|
||||
|
||||
# undef DO_FLS
|
||||
# undef DO_BSR64
|
||||
#else
|
||||
|
||||
static inline unsigned
|
||||
fls_llu(unsigned long long x) {
|
||||
return fls_llu_slow(x);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_lu(unsigned long x) {
|
||||
return fls_lu_slow(x);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
fls_u(unsigned x) {
|
||||
return fls_u_slow(x);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LG_SIZEOF_LONG_LONG > 3
|
||||
# error "Haven't implemented popcount for 16-byte ints."
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
#define DO_POPCOUNT(x, type) do { \
|
||||
/* \
|
||||
* Algorithm from an old AMD optimization reference manual. \
|
||||
* We're putting a little bit more work than you might expect \
|
||||
* into the no-instrinsic case, since we only support the \
|
||||
* GCC intrinsics spelling of popcount (for now). Detecting \
|
||||
* whether or not the popcount builtin is actually useable in \
|
||||
* MSVC is nontrivial. \
|
||||
*/ \
|
||||
\
|
||||
type bmul = (type)0x0101010101010101ULL; \
|
||||
\
|
||||
/* \
|
||||
* Replace each 2 bits with the sideways sum of the original \
|
||||
* values. 0x5 = 0b0101. \
|
||||
* \
|
||||
* You might expect this to be: \
|
||||
* x = (x & 0x55...) + ((x >> 1) & 0x55...). \
|
||||
* That costs an extra mask relative to this, though. \
|
||||
*/ \
|
||||
x = x - ((x >> 1) & (0x55U * bmul)); \
|
||||
/* Replace each 4 bits with their sideays sum. 0x3 = 0b0011. */\
|
||||
x = (x & (bmul * 0x33U)) + ((x >> 2) & (bmul * 0x33U)); \
|
||||
/* \
|
||||
* Replace each 8 bits with their sideways sum. Note that we \
|
||||
* can't overflow within each 4-bit sum here, so we can skip \
|
||||
* the initial mask. \
|
||||
*/ \
|
||||
x = (x + (x >> 4)) & (bmul * 0x0FU); \
|
||||
/* \
|
||||
* None of the partial sums in this multiplication (viewed in \
|
||||
* base-256) can overflow into the next digit. So the least \
|
||||
* significant byte of the product will be the least \
|
||||
* significant byte of the original value, the second least \
|
||||
* significant byte will be the sum of the two least \
|
||||
* significant bytes of the original value, and so on. \
|
||||
* Importantly, the high byte will be the byte-wise sum of all \
|
||||
* the bytes of the original value. \
|
||||
*/ \
|
||||
x = x * bmul; \
|
||||
x >>= ((sizeof(x) - 1) * 8); \
|
||||
return (unsigned)x; \
|
||||
} while(0)
|
||||
/* clang-format on */
|
||||
|
||||
static inline unsigned
|
||||
popcount_u_slow(unsigned bitmap) {
|
||||
DO_POPCOUNT(bitmap, unsigned);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
popcount_lu_slow(unsigned long bitmap) {
|
||||
DO_POPCOUNT(bitmap, unsigned long);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
popcount_llu_slow(unsigned long long bitmap) {
|
||||
DO_POPCOUNT(bitmap, unsigned long long);
|
||||
}
|
||||
|
||||
#undef DO_POPCOUNT
|
||||
|
||||
static inline unsigned
|
||||
popcount_u(unsigned bitmap) {
|
||||
#ifdef JEMALLOC_INTERNAL_POPCOUNT
|
||||
return JEMALLOC_INTERNAL_POPCOUNT(bitmap);
|
||||
#else
|
||||
return popcount_u_slow(bitmap);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
popcount_lu(unsigned long bitmap) {
|
||||
#ifdef JEMALLOC_INTERNAL_POPCOUNTL
|
||||
return JEMALLOC_INTERNAL_POPCOUNTL(bitmap);
|
||||
#else
|
||||
return popcount_lu_slow(bitmap);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
popcount_llu(unsigned long long bitmap) {
|
||||
#ifdef JEMALLOC_INTERNAL_POPCOUNTLL
|
||||
return JEMALLOC_INTERNAL_POPCOUNTLL(bitmap);
|
||||
#else
|
||||
return popcount_llu_slow(bitmap);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears first unset bit in bitmap, and returns
|
||||
* place of bit. bitmap *must not* be 0.
|
||||
*/
|
||||
|
||||
static inline size_t
|
||||
cfs_lu(unsigned long *bitmap) {
|
||||
util_assume(*bitmap != 0);
|
||||
size_t bit = ffs_lu(*bitmap);
|
||||
*bitmap ^= ZU(1) << bit;
|
||||
return bit;
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
ffs_zu(size_t x) {
|
||||
#if LG_SIZEOF_PTR == LG_SIZEOF_INT
|
||||
return ffs_u(bitmap);
|
||||
return ffs_u(x);
|
||||
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG
|
||||
return ffs_lu(bitmap);
|
||||
return ffs_lu(x);
|
||||
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG_LONG
|
||||
return ffs_llu(bitmap);
|
||||
return ffs_llu(x);
|
||||
#else
|
||||
#error No implementation for size_t ffs()
|
||||
# error No implementation for size_t ffs()
|
||||
#endif
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
ffs_u64(uint64_t bitmap) {
|
||||
static inline unsigned
|
||||
fls_zu(size_t x) {
|
||||
#if LG_SIZEOF_PTR == LG_SIZEOF_INT
|
||||
return fls_u(x);
|
||||
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG
|
||||
return fls_lu(x);
|
||||
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG_LONG
|
||||
return fls_llu(x);
|
||||
#else
|
||||
# error No implementation for size_t fls()
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
ffs_u64(uint64_t x) {
|
||||
#if LG_SIZEOF_LONG == 3
|
||||
return ffs_lu(bitmap);
|
||||
return ffs_lu(x);
|
||||
#elif LG_SIZEOF_LONG_LONG == 3
|
||||
return ffs_llu(bitmap);
|
||||
return ffs_llu(x);
|
||||
#else
|
||||
#error No implementation for 64-bit ffs()
|
||||
# error No implementation for 64-bit ffs()
|
||||
#endif
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
ffs_u32(uint32_t bitmap) {
|
||||
static inline unsigned
|
||||
fls_u64(uint64_t x) {
|
||||
#if LG_SIZEOF_LONG == 3
|
||||
return fls_lu(x);
|
||||
#elif LG_SIZEOF_LONG_LONG == 3
|
||||
return fls_llu(x);
|
||||
#else
|
||||
# error No implementation for 64-bit fls()
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
ffs_u32(uint32_t x) {
|
||||
#if LG_SIZEOF_INT == 2
|
||||
return ffs_u(bitmap);
|
||||
return ffs_u(x);
|
||||
#else
|
||||
#error No implementation for 32-bit ffs()
|
||||
# error No implementation for 32-bit ffs()
|
||||
#endif
|
||||
return ffs_u(bitmap);
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE uint64_t
|
||||
static inline unsigned
|
||||
fls_u32(uint32_t x) {
|
||||
#if LG_SIZEOF_INT == 2
|
||||
return fls_u(x);
|
||||
#else
|
||||
# error No implementation for 32-bit fls()
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
pow2_ceil_u64(uint64_t x) {
|
||||
x--;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
x |= x >> 32;
|
||||
x++;
|
||||
return x;
|
||||
if (unlikely(x <= 1)) {
|
||||
return x;
|
||||
}
|
||||
size_t msb_on_index = fls_u64(x - 1);
|
||||
/*
|
||||
* Range-check; it's on the callers to ensure that the result of this
|
||||
* call won't overflow.
|
||||
*/
|
||||
assert(msb_on_index < 63);
|
||||
return 1ULL << (msb_on_index + 1);
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE uint32_t
|
||||
static inline uint32_t
|
||||
pow2_ceil_u32(uint32_t x) {
|
||||
x--;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
x++;
|
||||
return x;
|
||||
if (unlikely(x <= 1)) {
|
||||
return x;
|
||||
}
|
||||
size_t msb_on_index = fls_u32(x - 1);
|
||||
/* As above. */
|
||||
assert(msb_on_index < 31);
|
||||
return 1U << (msb_on_index + 1);
|
||||
}
|
||||
|
||||
/* Compute the smallest power of 2 that is >= x. */
|
||||
BIT_UTIL_INLINE size_t
|
||||
static inline size_t
|
||||
pow2_ceil_zu(size_t x) {
|
||||
#if (LG_SIZEOF_PTR == 3)
|
||||
return pow2_ceil_u64(x);
|
||||
|
|
@ -96,70 +394,38 @@ pow2_ceil_zu(size_t x) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
|
||||
BIT_UTIL_INLINE unsigned
|
||||
static inline unsigned
|
||||
lg_floor(size_t x) {
|
||||
size_t ret;
|
||||
assert(x != 0);
|
||||
|
||||
asm ("bsr %1, %0"
|
||||
: "=r"(ret) // Outputs.
|
||||
: "r"(x) // Inputs.
|
||||
);
|
||||
assert(ret < UINT_MAX);
|
||||
return (unsigned)ret;
|
||||
}
|
||||
#elif (defined(_MSC_VER))
|
||||
BIT_UTIL_INLINE unsigned
|
||||
lg_floor(size_t x) {
|
||||
unsigned long ret;
|
||||
|
||||
assert(x != 0);
|
||||
|
||||
util_assume(x != 0);
|
||||
#if (LG_SIZEOF_PTR == 3)
|
||||
_BitScanReverse64(&ret, x);
|
||||
#elif (LG_SIZEOF_PTR == 2)
|
||||
_BitScanReverse(&ret, x);
|
||||
return fls_u64(x);
|
||||
#else
|
||||
# error "Unsupported type size for lg_floor()"
|
||||
#endif
|
||||
assert(ret < UINT_MAX);
|
||||
return (unsigned)ret;
|
||||
}
|
||||
#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
|
||||
BIT_UTIL_INLINE unsigned
|
||||
lg_floor(size_t x) {
|
||||
assert(x != 0);
|
||||
|
||||
#if (LG_SIZEOF_PTR == LG_SIZEOF_INT)
|
||||
return ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clz(x);
|
||||
#elif (LG_SIZEOF_PTR == LG_SIZEOF_LONG)
|
||||
return ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clzl(x);
|
||||
#else
|
||||
# error "Unsupported type size for lg_floor()"
|
||||
return fls_u32(x);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
BIT_UTIL_INLINE unsigned
|
||||
lg_floor(size_t x) {
|
||||
assert(x != 0);
|
||||
|
||||
x |= (x >> 1);
|
||||
x |= (x >> 2);
|
||||
x |= (x >> 4);
|
||||
x |= (x >> 8);
|
||||
x |= (x >> 16);
|
||||
#if (LG_SIZEOF_PTR == 3)
|
||||
x |= (x >> 32);
|
||||
#endif
|
||||
if (x == SIZE_T_MAX) {
|
||||
return (8 << LG_SIZEOF_PTR) - 1;
|
||||
}
|
||||
x++;
|
||||
return ffs_zu(x) - 2;
|
||||
static inline unsigned
|
||||
lg_ceil(size_t x) {
|
||||
return lg_floor(x) + ((x & (x - 1)) == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
/* A compile-time version of lg_floor and lg_ceil. */
|
||||
#define LG_FLOOR_1(x) 0
|
||||
#define LG_FLOOR_2(x) (x < (1ULL << 1) ? LG_FLOOR_1(x) : 1 + LG_FLOOR_1(x >> 1))
|
||||
#define LG_FLOOR_4(x) (x < (1ULL << 2) ? LG_FLOOR_2(x) : 2 + LG_FLOOR_2(x >> 2))
|
||||
#define LG_FLOOR_8(x) (x < (1ULL << 4) ? LG_FLOOR_4(x) : 4 + LG_FLOOR_4(x >> 4))
|
||||
#define LG_FLOOR_16(x) \
|
||||
(x < (1ULL << 8) ? LG_FLOOR_8(x) : 8 + LG_FLOOR_8(x >> 8))
|
||||
#define LG_FLOOR_32(x) \
|
||||
(x < (1ULL << 16) ? LG_FLOOR_16(x) : 16 + LG_FLOOR_16(x >> 16))
|
||||
#define LG_FLOOR_64(x) \
|
||||
(x < (1ULL << 32) ? LG_FLOOR_32(x) : 32 + LG_FLOOR_32(x >> 32))
|
||||
#if LG_SIZEOF_PTR == 2
|
||||
# define LG_FLOOR(x) LG_FLOOR_32((x))
|
||||
#else
|
||||
# define LG_FLOOR(x) LG_FLOOR_64((x))
|
||||
#endif
|
||||
|
||||
#undef BIT_UTIL_INLINE
|
||||
#define LG_CEIL(x) (LG_FLOOR(x) + (((x) & ((x) - 1)) == 0 ? 0 : 1))
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BIT_UTIL_H */
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BITMAP_H
|
||||
#define JEMALLOC_INTERNAL_BITMAP_H
|
||||
|
||||
#include "jemalloc/internal/arena_types.h"
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
typedef unsigned long bitmap_t;
|
||||
#define LG_SIZEOF_BITMAP LG_SIZEOF_LONG
|
||||
#define LG_SIZEOF_BITMAP LG_SIZEOF_LONG
|
||||
|
||||
/* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */
|
||||
#if LG_SLAB_MAXREGS > LG_CEIL_NSIZES
|
||||
#if SC_LG_SLAB_MAXREGS > LG_CEIL(SC_NSIZES)
|
||||
/* Maximum bitmap bit count is determined by maximum regions per slab. */
|
||||
# define LG_BITMAP_MAXBITS LG_SLAB_MAXREGS
|
||||
# define LG_BITMAP_MAXBITS SC_LG_SLAB_MAXREGS
|
||||
#else
|
||||
/* Maximum bitmap bit count is determined by number of extent size classes. */
|
||||
# define LG_BITMAP_MAXBITS LG_CEIL_NSIZES
|
||||
# define LG_BITMAP_MAXBITS LG_CEIL(SC_NSIZES)
|
||||
#endif
|
||||
#define BITMAP_MAXBITS (ZU(1) << LG_BITMAP_MAXBITS)
|
||||
#define BITMAP_MAXBITS (ZU(1) << LG_BITMAP_MAXBITS)
|
||||
|
||||
/* Number of bits per group. */
|
||||
#define LG_BITMAP_GROUP_NBITS (LG_SIZEOF_BITMAP + 3)
|
||||
#define BITMAP_GROUP_NBITS (1U << LG_BITMAP_GROUP_NBITS)
|
||||
#define BITMAP_GROUP_NBITS_MASK (BITMAP_GROUP_NBITS-1)
|
||||
#define LG_BITMAP_GROUP_NBITS (LG_SIZEOF_BITMAP + 3)
|
||||
#define BITMAP_GROUP_NBITS (1U << LG_BITMAP_GROUP_NBITS)
|
||||
#define BITMAP_GROUP_NBITS_MASK (BITMAP_GROUP_NBITS - 1)
|
||||
|
||||
/*
|
||||
* Do some analysis on how big the bitmap is before we use a tree. For a brute
|
||||
|
|
@ -29,67 +29,64 @@ typedef unsigned long bitmap_t;
|
|||
* use a tree instead.
|
||||
*/
|
||||
#if LG_BITMAP_MAXBITS - LG_BITMAP_GROUP_NBITS > 3
|
||||
# define BITMAP_USE_TREE
|
||||
# define BITMAP_USE_TREE
|
||||
#endif
|
||||
|
||||
/* Number of groups required to store a given number of bits. */
|
||||
#define BITMAP_BITS2GROUPS(nbits) \
|
||||
(((nbits) + BITMAP_GROUP_NBITS_MASK) >> LG_BITMAP_GROUP_NBITS)
|
||||
#define BITMAP_BITS2GROUPS(nbits) \
|
||||
(((nbits) + BITMAP_GROUP_NBITS_MASK) >> LG_BITMAP_GROUP_NBITS)
|
||||
|
||||
/*
|
||||
* Number of groups required at a particular level for a given number of bits.
|
||||
*/
|
||||
#define BITMAP_GROUPS_L0(nbits) \
|
||||
BITMAP_BITS2GROUPS(nbits)
|
||||
#define BITMAP_GROUPS_L1(nbits) \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(nbits))
|
||||
#define BITMAP_GROUPS_L2(nbits) \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))
|
||||
#define BITMAP_GROUPS_L3(nbits) \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS( \
|
||||
BITMAP_BITS2GROUPS((nbits)))))
|
||||
#define BITMAP_GROUPS_L4(nbits) \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS( \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))))
|
||||
#define BITMAP_GROUPS_L0(nbits) BITMAP_BITS2GROUPS(nbits)
|
||||
#define BITMAP_GROUPS_L1(nbits) BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(nbits))
|
||||
#define BITMAP_GROUPS_L2(nbits) \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))
|
||||
#define BITMAP_GROUPS_L3(nbits) \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS( \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits)))))
|
||||
#define BITMAP_GROUPS_L4(nbits) \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS( \
|
||||
BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))))
|
||||
|
||||
/*
|
||||
* Assuming the number of levels, number of groups required for a given number
|
||||
* of bits.
|
||||
*/
|
||||
#define BITMAP_GROUPS_1_LEVEL(nbits) \
|
||||
BITMAP_GROUPS_L0(nbits)
|
||||
#define BITMAP_GROUPS_2_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_1_LEVEL(nbits) + BITMAP_GROUPS_L1(nbits))
|
||||
#define BITMAP_GROUPS_3_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_2_LEVEL(nbits) + BITMAP_GROUPS_L2(nbits))
|
||||
#define BITMAP_GROUPS_4_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_3_LEVEL(nbits) + BITMAP_GROUPS_L3(nbits))
|
||||
#define BITMAP_GROUPS_5_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_4_LEVEL(nbits) + BITMAP_GROUPS_L4(nbits))
|
||||
#define BITMAP_GROUPS_1_LEVEL(nbits) BITMAP_GROUPS_L0(nbits)
|
||||
#define BITMAP_GROUPS_2_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_1_LEVEL(nbits) + BITMAP_GROUPS_L1(nbits))
|
||||
#define BITMAP_GROUPS_3_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_2_LEVEL(nbits) + BITMAP_GROUPS_L2(nbits))
|
||||
#define BITMAP_GROUPS_4_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_3_LEVEL(nbits) + BITMAP_GROUPS_L3(nbits))
|
||||
#define BITMAP_GROUPS_5_LEVEL(nbits) \
|
||||
(BITMAP_GROUPS_4_LEVEL(nbits) + BITMAP_GROUPS_L4(nbits))
|
||||
|
||||
/*
|
||||
* Maximum number of groups required to support LG_BITMAP_MAXBITS.
|
||||
*/
|
||||
#ifdef BITMAP_USE_TREE
|
||||
|
||||
#if LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_1_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_1_LEVEL(BITMAP_MAXBITS)
|
||||
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 2
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_2_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_2_LEVEL(BITMAP_MAXBITS)
|
||||
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 3
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_3_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_3_LEVEL(BITMAP_MAXBITS)
|
||||
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 4
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_4_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_4_LEVEL(BITMAP_MAXBITS)
|
||||
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 5
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_5_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_5_LEVEL(BITMAP_MAXBITS)
|
||||
#else
|
||||
# error "Unsupported bitmap size"
|
||||
#endif
|
||||
# if LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_1_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_1_LEVEL(BITMAP_MAXBITS)
|
||||
# elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 2
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_2_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_2_LEVEL(BITMAP_MAXBITS)
|
||||
# elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 3
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_3_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_3_LEVEL(BITMAP_MAXBITS)
|
||||
# elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 4
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_4_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_4_LEVEL(BITMAP_MAXBITS)
|
||||
# elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 5
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_GROUPS_5_LEVEL(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_GROUPS_5_LEVEL(BITMAP_MAXBITS)
|
||||
# else
|
||||
# error "Unsupported bitmap size"
|
||||
# endif
|
||||
|
||||
/*
|
||||
* Maximum number of levels possible. This could be statically computed based
|
||||
|
|
@ -105,42 +102,53 @@ typedef unsigned long bitmap_t;
|
|||
* unused trailing entries in bitmap_info_t structures; the bitmaps themselves
|
||||
* are not impacted.
|
||||
*/
|
||||
#define BITMAP_MAX_LEVELS 5
|
||||
# define BITMAP_MAX_LEVELS 5
|
||||
|
||||
#define BITMAP_INFO_INITIALIZER(nbits) { \
|
||||
/* nbits. */ \
|
||||
nbits, \
|
||||
/* nlevels. */ \
|
||||
(BITMAP_GROUPS_L0(nbits) > BITMAP_GROUPS_L1(nbits)) + \
|
||||
(BITMAP_GROUPS_L1(nbits) > BITMAP_GROUPS_L2(nbits)) + \
|
||||
(BITMAP_GROUPS_L2(nbits) > BITMAP_GROUPS_L3(nbits)) + \
|
||||
(BITMAP_GROUPS_L3(nbits) > BITMAP_GROUPS_L4(nbits)) + 1, \
|
||||
/* levels. */ \
|
||||
{ \
|
||||
{0}, \
|
||||
{BITMAP_GROUPS_L0(nbits)}, \
|
||||
{BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)}, \
|
||||
{BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits) + \
|
||||
BITMAP_GROUPS_L0(nbits)}, \
|
||||
{BITMAP_GROUPS_L3(nbits) + BITMAP_GROUPS_L2(nbits) + \
|
||||
BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)}, \
|
||||
{BITMAP_GROUPS_L4(nbits) + BITMAP_GROUPS_L3(nbits) + \
|
||||
BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits) \
|
||||
+ BITMAP_GROUPS_L0(nbits)} \
|
||||
} \
|
||||
}
|
||||
# define BITMAP_INFO_INITIALIZER(nbits) \
|
||||
{ \
|
||||
/* nbits. */ \
|
||||
nbits, /* nlevels. */ \
|
||||
(BITMAP_GROUPS_L0(nbits) \
|
||||
> BITMAP_GROUPS_L1(nbits)) \
|
||||
+ (BITMAP_GROUPS_L1(nbits) \
|
||||
> BITMAP_GROUPS_L2(nbits)) \
|
||||
+ (BITMAP_GROUPS_L2(nbits) \
|
||||
> BITMAP_GROUPS_L3(nbits)) \
|
||||
+ (BITMAP_GROUPS_L3(nbits) \
|
||||
> BITMAP_GROUPS_L4(nbits)) \
|
||||
+ 1, /* levels. */ \
|
||||
{ \
|
||||
{0}, {BITMAP_GROUPS_L0(nbits)}, \
|
||||
{BITMAP_GROUPS_L1(nbits) \
|
||||
+ BITMAP_GROUPS_L0(nbits)}, \
|
||||
{BITMAP_GROUPS_L2(nbits) \
|
||||
+ BITMAP_GROUPS_L1(nbits) \
|
||||
+ BITMAP_GROUPS_L0(nbits)}, \
|
||||
{BITMAP_GROUPS_L3(nbits) \
|
||||
+ BITMAP_GROUPS_L2(nbits) \
|
||||
+ BITMAP_GROUPS_L1(nbits) \
|
||||
+ BITMAP_GROUPS_L0(nbits)}, \
|
||||
{ \
|
||||
BITMAP_GROUPS_L4(nbits) \
|
||||
+ BITMAP_GROUPS_L3(nbits) \
|
||||
+ BITMAP_GROUPS_L2(nbits) \
|
||||
+ BITMAP_GROUPS_L1(nbits) \
|
||||
+ BITMAP_GROUPS_L0(nbits) \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#else /* BITMAP_USE_TREE */
|
||||
|
||||
#define BITMAP_GROUPS(nbits) BITMAP_BITS2GROUPS(nbits)
|
||||
#define BITMAP_GROUPS_MAX BITMAP_BITS2GROUPS(BITMAP_MAXBITS)
|
||||
# define BITMAP_GROUPS(nbits) BITMAP_BITS2GROUPS(nbits)
|
||||
# define BITMAP_GROUPS_MAX BITMAP_BITS2GROUPS(BITMAP_MAXBITS)
|
||||
|
||||
#define BITMAP_INFO_INITIALIZER(nbits) { \
|
||||
/* nbits. */ \
|
||||
nbits, \
|
||||
/* ngroups. */ \
|
||||
BITMAP_BITS2GROUPS(nbits) \
|
||||
}
|
||||
# define BITMAP_INFO_INITIALIZER(nbits) \
|
||||
{ \
|
||||
/* nbits. */ \
|
||||
nbits, /* ngroups. */ \
|
||||
BITMAP_BITS2GROUPS(nbits) \
|
||||
}
|
||||
|
||||
#endif /* BITMAP_USE_TREE */
|
||||
|
||||
|
|
@ -161,21 +169,21 @@ typedef struct bitmap_info_s {
|
|||
* Only the first (nlevels+1) elements are used, and levels are ordered
|
||||
* bottom to top (e.g. the bottom level is stored in levels[0]).
|
||||
*/
|
||||
bitmap_level_t levels[BITMAP_MAX_LEVELS+1];
|
||||
#else /* BITMAP_USE_TREE */
|
||||
bitmap_level_t levels[BITMAP_MAX_LEVELS + 1];
|
||||
#else /* BITMAP_USE_TREE */
|
||||
/* Number of groups necessary for nbits. */
|
||||
size_t ngroups;
|
||||
#endif /* BITMAP_USE_TREE */
|
||||
} bitmap_info_t;
|
||||
|
||||
void bitmap_info_init(bitmap_info_t *binfo, size_t nbits);
|
||||
void bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill);
|
||||
void bitmap_info_init(bitmap_info_t *binfo, size_t nbits);
|
||||
void bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill);
|
||||
size_t bitmap_size(const bitmap_info_t *binfo);
|
||||
|
||||
static inline bool
|
||||
bitmap_full(bitmap_t *bitmap, const bitmap_info_t *binfo) {
|
||||
#ifdef BITMAP_USE_TREE
|
||||
size_t rgoff = binfo->levels[binfo->nlevels].group_offset - 1;
|
||||
size_t rgoff = binfo->levels[binfo->nlevels].group_offset - 1;
|
||||
bitmap_t rg = bitmap[rgoff];
|
||||
/* The bitmap is full iff the root group is 0. */
|
||||
return (rg == 0);
|
||||
|
|
@ -193,7 +201,7 @@ bitmap_full(bitmap_t *bitmap, const bitmap_info_t *binfo) {
|
|||
|
||||
static inline bool
|
||||
bitmap_get(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {
|
||||
size_t goff;
|
||||
size_t goff;
|
||||
bitmap_t g;
|
||||
|
||||
assert(bit < binfo->nbits);
|
||||
|
|
@ -204,9 +212,9 @@ bitmap_get(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {
|
|||
|
||||
static inline void
|
||||
bitmap_set(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {
|
||||
size_t goff;
|
||||
size_t goff;
|
||||
bitmap_t *gp;
|
||||
bitmap_t g;
|
||||
bitmap_t g;
|
||||
|
||||
assert(bit < binfo->nbits);
|
||||
assert(!bitmap_get(bitmap, binfo, bit));
|
||||
|
|
@ -245,12 +253,13 @@ bitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {
|
|||
#ifdef BITMAP_USE_TREE
|
||||
size_t bit = 0;
|
||||
for (unsigned level = binfo->nlevels; level--;) {
|
||||
size_t lg_bits_per_group = (LG_BITMAP_GROUP_NBITS * (level +
|
||||
1));
|
||||
bitmap_t group = bitmap[binfo->levels[level].group_offset + (bit
|
||||
>> lg_bits_per_group)];
|
||||
unsigned group_nmask = (unsigned)(((min_bit > bit) ? (min_bit -
|
||||
bit) : 0) >> (lg_bits_per_group - LG_BITMAP_GROUP_NBITS));
|
||||
size_t lg_bits_per_group = (LG_BITMAP_GROUP_NBITS
|
||||
* (level + 1));
|
||||
bitmap_t group = bitmap[binfo->levels[level].group_offset
|
||||
+ (bit >> lg_bits_per_group)];
|
||||
unsigned group_nmask =
|
||||
(unsigned)(((min_bit > bit) ? (min_bit - bit) : 0)
|
||||
>> (lg_bits_per_group - LG_BITMAP_GROUP_NBITS));
|
||||
assert(group_nmask <= BITMAP_GROUP_NBITS);
|
||||
bitmap_t group_mask = ~((1LU << group_nmask) - 1);
|
||||
bitmap_t group_masked = group & group_mask;
|
||||
|
|
@ -273,25 +282,28 @@ bitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {
|
|||
}
|
||||
return bitmap_ffu(bitmap, binfo, sib_base);
|
||||
}
|
||||
bit += ((size_t)(ffs_lu(group_masked) - 1)) <<
|
||||
(lg_bits_per_group - LG_BITMAP_GROUP_NBITS);
|
||||
bit += ((size_t)ffs_lu(group_masked))
|
||||
<< (lg_bits_per_group - LG_BITMAP_GROUP_NBITS);
|
||||
}
|
||||
assert(bit >= min_bit);
|
||||
assert(bit < binfo->nbits);
|
||||
return bit;
|
||||
#else
|
||||
size_t i = min_bit >> LG_BITMAP_GROUP_NBITS;
|
||||
bitmap_t g = bitmap[i] & ~((1LU << (min_bit & BITMAP_GROUP_NBITS_MASK))
|
||||
- 1);
|
||||
size_t i = min_bit >> LG_BITMAP_GROUP_NBITS;
|
||||
bitmap_t g = bitmap[i]
|
||||
& ~((1LU << (min_bit & BITMAP_GROUP_NBITS_MASK)) - 1);
|
||||
size_t bit;
|
||||
do {
|
||||
bit = ffs_lu(g);
|
||||
if (bit != 0) {
|
||||
return (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);
|
||||
while (1) {
|
||||
if (g != 0) {
|
||||
bit = ffs_lu(g);
|
||||
return (i << LG_BITMAP_GROUP_NBITS) + bit;
|
||||
}
|
||||
i++;
|
||||
if (i >= binfo->ngroups) {
|
||||
break;
|
||||
}
|
||||
g = bitmap[i];
|
||||
} while (i < binfo->ngroups);
|
||||
}
|
||||
return binfo->nbits;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -299,7 +311,7 @@ bitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {
|
|||
/* sfu: set first unset. */
|
||||
static inline size_t
|
||||
bitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) {
|
||||
size_t bit;
|
||||
size_t bit;
|
||||
bitmap_t g;
|
||||
unsigned i;
|
||||
|
||||
|
|
@ -308,20 +320,20 @@ bitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) {
|
|||
#ifdef BITMAP_USE_TREE
|
||||
i = binfo->nlevels - 1;
|
||||
g = bitmap[binfo->levels[i].group_offset];
|
||||
bit = ffs_lu(g) - 1;
|
||||
bit = ffs_lu(g);
|
||||
while (i > 0) {
|
||||
i--;
|
||||
g = bitmap[binfo->levels[i].group_offset + bit];
|
||||
bit = (bit << LG_BITMAP_GROUP_NBITS) + (ffs_lu(g) - 1);
|
||||
bit = (bit << LG_BITMAP_GROUP_NBITS) + ffs_lu(g);
|
||||
}
|
||||
#else
|
||||
i = 0;
|
||||
g = bitmap[0];
|
||||
while ((bit = ffs_lu(g)) == 0) {
|
||||
while (g == 0) {
|
||||
i++;
|
||||
g = bitmap[i];
|
||||
}
|
||||
bit = (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);
|
||||
bit = (i << LG_BITMAP_GROUP_NBITS) + ffs_lu(g);
|
||||
#endif
|
||||
bitmap_set(bitmap, binfo, bit);
|
||||
return bit;
|
||||
|
|
@ -329,9 +341,9 @@ bitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) {
|
|||
|
||||
static inline void
|
||||
bitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {
|
||||
size_t goff;
|
||||
bitmap_t *gp;
|
||||
bitmap_t g;
|
||||
size_t goff;
|
||||
bitmap_t *gp;
|
||||
bitmap_t g;
|
||||
UNUSED bool propagate;
|
||||
|
||||
assert(bit < binfo->nbits);
|
||||
|
|
|
|||
36
include/jemalloc/internal/buf_writer.h
Normal file
36
include/jemalloc/internal/buf_writer.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef JEMALLOC_INTERNAL_BUF_WRITER_H
|
||||
#define JEMALLOC_INTERNAL_BUF_WRITER_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
/*
|
||||
* Note: when using the buffered writer, cbopaque is passed to write_cb only
|
||||
* when the buffer is flushed. It would make a difference if cbopaque points
|
||||
* to something that's changing for each write_cb call, or something that
|
||||
* affects write_cb in a way dependent on the content of the output string.
|
||||
* However, the most typical usage case in practice is that cbopaque points to
|
||||
* some "option like" content for the write_cb, so it doesn't matter.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
write_cb_t *write_cb;
|
||||
void *cbopaque;
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
size_t buf_end;
|
||||
bool internal_buf;
|
||||
} buf_writer_t;
|
||||
|
||||
bool buf_writer_init(tsdn_t *tsdn, buf_writer_t *buf_writer,
|
||||
write_cb_t *write_cb, void *cbopaque, char *buf, size_t buf_len);
|
||||
void buf_writer_flush(buf_writer_t *buf_writer);
|
||||
write_cb_t buf_writer_cb;
|
||||
void buf_writer_terminate(tsdn_t *tsdn, buf_writer_t *buf_writer);
|
||||
|
||||
typedef ssize_t(read_cb_t)(void *read_cbopaque, void *buf, size_t limit);
|
||||
void buf_writer_pipe(
|
||||
buf_writer_t *buf_writer, read_cb_t *read_cb, void *read_cbopaque);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BUF_WRITER_H */
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
#ifndef JEMALLOC_INTERNAL_CACHE_BIN_H
|
||||
#define JEMALLOC_INTERNAL_CACHE_BIN_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_externs.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/safety_check.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
|
||||
/*
|
||||
* The cache_bins are the mechanism that the tcache and the arena use to
|
||||
|
|
@ -13,14 +17,42 @@
|
|||
* of the tcache at all.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The size in bytes of each cache bin stack. We also use this to indicate
|
||||
* *counts* of individual objects.
|
||||
*/
|
||||
typedef uint16_t cache_bin_sz_t;
|
||||
|
||||
#define JUNK_ADDR ((uintptr_t)0x7a7a7a7a7a7a7a7aULL)
|
||||
/*
|
||||
* Leave a noticeable mark pattern on the cache bin stack boundaries, in case a
|
||||
* bug starts leaking those. Make it look like the junk pattern but be distinct
|
||||
* from it.
|
||||
*/
|
||||
static const uintptr_t cache_bin_preceding_junk = JUNK_ADDR;
|
||||
/* Note: JUNK_ADDR vs. JUNK_ADDR + 1 -- this tells you which pointer leaked. */
|
||||
static const uintptr_t cache_bin_trailing_junk = JUNK_ADDR + 1;
|
||||
/*
|
||||
* A pointer used to initialize a fake stack_head for disabled small bins
|
||||
* so that the enabled/disabled assessment does not rely on ncached_max.
|
||||
*/
|
||||
extern const uintptr_t disabled_bin;
|
||||
|
||||
/*
|
||||
* The count of the number of cached allocations in a bin. We make this signed
|
||||
* so that negative numbers can encode "invalid" states (e.g. a low water mark
|
||||
* of -1 for a cache that has been depleted).
|
||||
* That implies the following value, for the maximum number of items in any
|
||||
* individual bin. The cache bins track their bounds looking just at the low
|
||||
* bits of a pointer, compared against a cache_bin_sz_t. So that's
|
||||
* 1 << (sizeof(cache_bin_sz_t) * 8)
|
||||
* bytes spread across pointer sized objects to get the maximum.
|
||||
*/
|
||||
typedef int32_t cache_bin_sz_t;
|
||||
#define CACHE_BIN_NCACHED_MAX \
|
||||
(((size_t)1 << sizeof(cache_bin_sz_t) * 8) / sizeof(void *) - 1)
|
||||
|
||||
/*
|
||||
* This lives inside the cache_bin (for locality reasons), and is initialized
|
||||
* alongside it, but is otherwise not modified by any cache bin operations.
|
||||
* It's logically public and maintained by its callers.
|
||||
*/
|
||||
typedef struct cache_bin_stats_s cache_bin_stats_t;
|
||||
struct cache_bin_stats_s {
|
||||
/*
|
||||
|
|
@ -36,34 +68,78 @@ struct cache_bin_stats_s {
|
|||
*/
|
||||
typedef struct cache_bin_info_s cache_bin_info_t;
|
||||
struct cache_bin_info_s {
|
||||
/* Upper limit on ncached. */
|
||||
cache_bin_sz_t ncached_max;
|
||||
};
|
||||
|
||||
/*
|
||||
* Responsible for caching allocations associated with a single size.
|
||||
*
|
||||
* Several pointers are used to track the stack. To save on metadata bytes,
|
||||
* only the stack_head is a full sized pointer (which is dereferenced on the
|
||||
* fastpath), while the others store only the low 16 bits -- this is correct
|
||||
* because a single stack never takes more space than 2^16 bytes, and at the
|
||||
* same time only equality checks are performed on the low bits.
|
||||
*
|
||||
* (low addr) (high addr)
|
||||
* |------stashed------|------available------|------cached-----|
|
||||
* ^ ^ ^ ^
|
||||
* low_bound(derived) low_bits_full stack_head low_bits_empty
|
||||
*/
|
||||
typedef struct cache_bin_s cache_bin_t;
|
||||
struct cache_bin_s {
|
||||
/* Min # cached since last GC. */
|
||||
cache_bin_sz_t low_water;
|
||||
/* # of cached objects. */
|
||||
cache_bin_sz_t ncached;
|
||||
/*
|
||||
* ncached and stats are both modified frequently. Let's keep them
|
||||
* The stack grows down. Whenever the bin is nonempty, the head points
|
||||
* to an array entry containing a valid allocation. When it is empty,
|
||||
* the head points to one element past the owned array.
|
||||
*/
|
||||
void **stack_head;
|
||||
/*
|
||||
* cur_ptr and stats are both modified frequently. Let's keep them
|
||||
* close so that they have a higher chance of being on the same
|
||||
* cacheline, thus less write-backs.
|
||||
*/
|
||||
cache_bin_stats_t tstats;
|
||||
|
||||
/*
|
||||
* Stack of available objects.
|
||||
* The low bits of the address of the first item in the stack that
|
||||
* hasn't been used since the last GC, to track the low water mark (min
|
||||
* # of cached items).
|
||||
*
|
||||
* To make use of adjacent cacheline prefetch, the items in the avail
|
||||
* stack goes to higher address for newer allocations. avail points
|
||||
* just above the available space, which means that
|
||||
* avail[-ncached, ... -1] are available items and the lowest item will
|
||||
* be allocated first.
|
||||
* Since the stack grows down, this is a higher address than
|
||||
* low_bits_full.
|
||||
*/
|
||||
void **avail;
|
||||
cache_bin_sz_t low_bits_low_water;
|
||||
|
||||
/*
|
||||
* The low bits of the value that stack_head will take on when the array
|
||||
* is full (of cached & stashed items). But remember that stack_head
|
||||
* always points to a valid item when the array is nonempty -- this is
|
||||
* in the array.
|
||||
*
|
||||
* Recall that since the stack grows down, this is the lowest available
|
||||
* address in the array for caching. Only adjusted when stashing items.
|
||||
*/
|
||||
cache_bin_sz_t low_bits_full;
|
||||
|
||||
/*
|
||||
* The low bits of the value that stack_head will take on when the array
|
||||
* is empty.
|
||||
*
|
||||
* The stack grows down -- this is one past the highest address in the
|
||||
* array. Immutable after initialization.
|
||||
*/
|
||||
cache_bin_sz_t low_bits_empty;
|
||||
|
||||
/* The maximum number of cached items in the bin. */
|
||||
cache_bin_info_t bin_info;
|
||||
};
|
||||
|
||||
/*
|
||||
* The cache_bins live inside the tcache, but the arena (by design) isn't
|
||||
* supposed to know much about tcache internals. To let the arena iterate over
|
||||
* associated bins, we keep (with the tcache) a linked list of
|
||||
* cache_bin_array_descriptor_ts that tell the arena how to find the bins.
|
||||
*/
|
||||
typedef struct cache_bin_array_descriptor_s cache_bin_array_descriptor_t;
|
||||
struct cache_bin_array_descriptor_s {
|
||||
/*
|
||||
|
|
@ -72,27 +148,238 @@ struct cache_bin_array_descriptor_s {
|
|||
*/
|
||||
ql_elm(cache_bin_array_descriptor_t) link;
|
||||
/* Pointers to the tcache bins. */
|
||||
cache_bin_t *bins_small;
|
||||
cache_bin_t *bins_large;
|
||||
cache_bin_t *bins;
|
||||
};
|
||||
|
||||
static inline void
|
||||
cache_bin_array_descriptor_init(cache_bin_array_descriptor_t *descriptor,
|
||||
cache_bin_t *bins_small, cache_bin_t *bins_large) {
|
||||
cache_bin_array_descriptor_init(
|
||||
cache_bin_array_descriptor_t *descriptor, cache_bin_t *bins) {
|
||||
ql_elm_new(descriptor, link);
|
||||
descriptor->bins_small = bins_small;
|
||||
descriptor->bins_large = bins_large;
|
||||
descriptor->bins = bins;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
cache_bin_nonfast_aligned(const void *ptr) {
|
||||
if (!config_uaf_detection) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Currently we use alignment to decide which pointer to junk & stash on
|
||||
* dealloc (for catching use-after-free). In some common cases a
|
||||
* page-aligned check is needed already (sdalloc w/ config_prof), so we
|
||||
* are getting it more or less for free -- no added instructions on
|
||||
* free_fastpath.
|
||||
*
|
||||
* Another way of deciding which pointer to sample, is adding another
|
||||
* thread_event to pick one every N bytes. That also adds no cost on
|
||||
* the fastpath, however it will tend to pick large allocations which is
|
||||
* not the desired behavior.
|
||||
*/
|
||||
return ((uintptr_t)ptr & san_cache_bin_nonfast_mask) == 0;
|
||||
}
|
||||
|
||||
static inline const void *
|
||||
cache_bin_disabled_bin_stack(void) {
|
||||
return &disabled_bin;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a cache bin was zero initialized (either because it lives in static or
|
||||
* thread-local storage, or was memset to 0), this function indicates whether or
|
||||
* not cache_bin_init was called on it.
|
||||
*/
|
||||
static inline bool
|
||||
cache_bin_still_zero_initialized(cache_bin_t *bin) {
|
||||
return bin->stack_head == NULL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
cache_bin_disabled(cache_bin_t *bin) {
|
||||
bool disabled = (bin->stack_head == cache_bin_disabled_bin_stack());
|
||||
if (disabled) {
|
||||
assert((uintptr_t)(*bin->stack_head) == JUNK_ADDR);
|
||||
}
|
||||
return disabled;
|
||||
}
|
||||
|
||||
/* Gets ncached_max without asserting that the bin is enabled. */
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_ncached_max_get_unsafe(cache_bin_t *bin) {
|
||||
return bin->bin_info.ncached_max;
|
||||
}
|
||||
|
||||
/* Returns ncached_max: Upper limit on ncached. */
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_ncached_max_get(cache_bin_t *bin) {
|
||||
assert(!cache_bin_disabled(bin));
|
||||
return cache_bin_ncached_max_get_unsafe(bin);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal.
|
||||
*
|
||||
* Asserts that the pointer associated with earlier is <= the one associated
|
||||
* with later.
|
||||
*/
|
||||
static inline void
|
||||
cache_bin_assert_earlier(
|
||||
cache_bin_t *bin, cache_bin_sz_t earlier, cache_bin_sz_t later) {
|
||||
if (earlier > later) {
|
||||
assert(bin->low_bits_full > bin->low_bits_empty);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal.
|
||||
*
|
||||
* Does difference calculations that handle wraparound correctly. Earlier must
|
||||
* be associated with the position earlier in memory.
|
||||
*/
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_diff(cache_bin_t *bin, cache_bin_sz_t earlier, cache_bin_sz_t later) {
|
||||
cache_bin_assert_earlier(bin, earlier, later);
|
||||
return later - earlier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Number of items currently cached in the bin, without checking ncached_max.
|
||||
*/
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_ncached_get_internal(cache_bin_t *bin) {
|
||||
cache_bin_sz_t diff = cache_bin_diff(bin,
|
||||
(cache_bin_sz_t)(uintptr_t)bin->stack_head, bin->low_bits_empty);
|
||||
cache_bin_sz_t n = diff / sizeof(void *);
|
||||
/*
|
||||
* We have undefined behavior here; if this function is called from the
|
||||
* arena stats updating code, then stack_head could change from the
|
||||
* first line to the next one. Morally, these loads should be atomic,
|
||||
* but compilers won't currently generate comparisons with in-memory
|
||||
* operands against atomics, and these variables get accessed on the
|
||||
* fast paths. This should still be "safe" in the sense of generating
|
||||
* the correct assembly for the foreseeable future, though.
|
||||
*/
|
||||
assert(n == 0 || *(bin->stack_head) != NULL);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Number of items currently cached in the bin, with checking ncached_max. The
|
||||
* caller must know that no concurrent modification of the cache_bin is
|
||||
* possible.
|
||||
*/
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_ncached_get_local(cache_bin_t *bin) {
|
||||
cache_bin_sz_t n = cache_bin_ncached_get_internal(bin);
|
||||
assert(n <= cache_bin_ncached_max_get(bin));
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal.
|
||||
*
|
||||
* A pointer to the position one past the end of the backing array.
|
||||
*
|
||||
* Do not call if racy, because both 'bin->stack_head' and 'bin->low_bits_full'
|
||||
* are subject to concurrent modifications.
|
||||
*/
|
||||
static inline void **
|
||||
cache_bin_empty_position_get(cache_bin_t *bin) {
|
||||
cache_bin_sz_t diff = cache_bin_diff(bin,
|
||||
(cache_bin_sz_t)(uintptr_t)bin->stack_head, bin->low_bits_empty);
|
||||
byte_t *empty_bits = (byte_t *)bin->stack_head + diff;
|
||||
void **ret = (void **)empty_bits;
|
||||
|
||||
assert(ret >= bin->stack_head);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal.
|
||||
*
|
||||
* Calculates low bits of the lower bound of the usable cache bin's range (see
|
||||
* cache_bin_t visual representation above).
|
||||
*
|
||||
* No values are concurrently modified, so should be safe to read in a
|
||||
* multithreaded environment. Currently concurrent access happens only during
|
||||
* arena statistics collection.
|
||||
*/
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_low_bits_low_bound_get(cache_bin_t *bin) {
|
||||
return (cache_bin_sz_t)bin->low_bits_empty
|
||||
- cache_bin_ncached_max_get(bin) * sizeof(void *);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal.
|
||||
*
|
||||
* A pointer to the position with the lowest address of the backing array.
|
||||
*/
|
||||
static inline void **
|
||||
cache_bin_low_bound_get(cache_bin_t *bin) {
|
||||
cache_bin_sz_t ncached_max = cache_bin_ncached_max_get(bin);
|
||||
void **ret = cache_bin_empty_position_get(bin) - ncached_max;
|
||||
assert(ret <= bin->stack_head);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* As the name implies. This is important since it's not correct to try to
|
||||
* batch fill a nonempty cache bin.
|
||||
*/
|
||||
static inline void
|
||||
cache_bin_assert_empty(cache_bin_t *bin) {
|
||||
assert(cache_bin_ncached_get_local(bin) == 0);
|
||||
assert(cache_bin_empty_position_get(bin) == bin->stack_head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get low water, but without any of the correctness checking we do for the
|
||||
* caller-usable version, if we are temporarily breaking invariants (like
|
||||
* ncached >= low_water during flush).
|
||||
*/
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_low_water_get_internal(cache_bin_t *bin) {
|
||||
return cache_bin_diff(bin, bin->low_bits_low_water, bin->low_bits_empty)
|
||||
/ sizeof(void *);
|
||||
}
|
||||
|
||||
/* Returns the numeric value of low water in [0, ncached]. */
|
||||
static inline cache_bin_sz_t
|
||||
cache_bin_low_water_get(cache_bin_t *bin) {
|
||||
cache_bin_sz_t low_water = cache_bin_low_water_get_internal(bin);
|
||||
assert(low_water <= cache_bin_ncached_max_get(bin));
|
||||
assert(low_water <= cache_bin_ncached_get_local(bin));
|
||||
|
||||
cache_bin_assert_earlier(bin,
|
||||
(cache_bin_sz_t)(uintptr_t)bin->stack_head,
|
||||
bin->low_bits_low_water);
|
||||
|
||||
return low_water;
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicates that the current cache bin position should be the low water mark
|
||||
* going forward.
|
||||
*/
|
||||
static inline void
|
||||
cache_bin_low_water_set(cache_bin_t *bin) {
|
||||
assert(!cache_bin_disabled(bin));
|
||||
bin->low_bits_low_water = (cache_bin_sz_t)(uintptr_t)bin->stack_head;
|
||||
}
|
||||
|
||||
static inline void
|
||||
cache_bin_low_water_adjust(cache_bin_t *bin) {
|
||||
assert(!cache_bin_disabled(bin));
|
||||
if (cache_bin_ncached_get_internal(bin)
|
||||
< cache_bin_low_water_get_internal(bin)) {
|
||||
cache_bin_low_water_set(bin);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
|
||||
void *ret;
|
||||
|
||||
if (unlikely(bin->ncached == 0)) {
|
||||
bin->low_water = -1;
|
||||
*success = false;
|
||||
return NULL;
|
||||
}
|
||||
cache_bin_alloc_impl(cache_bin_t *bin, bool *success, bool adjust_low_water) {
|
||||
/*
|
||||
* success (instead of ret) should be checked upon the return of this
|
||||
* function. We avoid checking (ret == NULL) because there is never a
|
||||
|
|
@ -100,15 +387,391 @@ cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
|
|||
* and eagerly checking ret would cause pipeline stall (waiting for the
|
||||
* cacheline).
|
||||
*/
|
||||
*success = true;
|
||||
ret = *(bin->avail - bin->ncached);
|
||||
bin->ncached--;
|
||||
|
||||
if (unlikely(bin->ncached < bin->low_water)) {
|
||||
bin->low_water = bin->ncached;
|
||||
/*
|
||||
* This may read from the empty position; however the loaded value won't
|
||||
* be used. It's safe because the stack has one more slot reserved.
|
||||
*/
|
||||
void *ret = *bin->stack_head;
|
||||
cache_bin_sz_t low_bits = (cache_bin_sz_t)(uintptr_t)bin->stack_head;
|
||||
void **new_head = bin->stack_head + 1;
|
||||
|
||||
/*
|
||||
* Note that the low water mark is at most empty; if we pass this check,
|
||||
* we know we're non-empty.
|
||||
*/
|
||||
if (likely(low_bits != bin->low_bits_low_water)) {
|
||||
bin->stack_head = new_head;
|
||||
*success = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
if (!adjust_low_water) {
|
||||
*success = false;
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* In the fast-path case where we call alloc_easy and then alloc, the
|
||||
* previous checking and computation is optimized away -- we didn't
|
||||
* actually commit any of our operations.
|
||||
*/
|
||||
if (likely(low_bits != bin->low_bits_empty)) {
|
||||
bin->stack_head = new_head;
|
||||
bin->low_bits_low_water = (cache_bin_sz_t)(uintptr_t)new_head;
|
||||
*success = true;
|
||||
return ret;
|
||||
}
|
||||
*success = false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an item out of the bin, failing if we're at the low-water mark.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
|
||||
/* We don't look at info if we're not adjusting low-water. */
|
||||
return cache_bin_alloc_impl(bin, success, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an item out of the bin, even if we're currently at the low-water
|
||||
* mark (and failing only if the bin is empty).
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
cache_bin_alloc(cache_bin_t *bin, bool *success) {
|
||||
return cache_bin_alloc_impl(bin, success, true);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE cache_bin_sz_t
|
||||
cache_bin_alloc_batch(cache_bin_t *bin, size_t num, void **out) {
|
||||
cache_bin_sz_t n = cache_bin_ncached_get_internal(bin);
|
||||
if (n > num) {
|
||||
n = (cache_bin_sz_t)num;
|
||||
}
|
||||
memcpy(out, bin->stack_head, n * sizeof(void *));
|
||||
bin->stack_head += n;
|
||||
cache_bin_low_water_adjust(bin);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
cache_bin_full(cache_bin_t *bin) {
|
||||
return (
|
||||
(cache_bin_sz_t)(uintptr_t)bin->stack_head == bin->low_bits_full);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scans the allocated area of the cache_bin for the given pointer up to limit.
|
||||
* Fires safety_check_fail if the ptr is found and returns true.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
cache_bin_dalloc_safety_checks(cache_bin_t *bin, void *ptr) {
|
||||
if (!config_debug || opt_debug_double_free_max_scan == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cache_bin_sz_t ncached = cache_bin_ncached_get_internal(bin);
|
||||
unsigned max_scan = opt_debug_double_free_max_scan < ncached
|
||||
? opt_debug_double_free_max_scan
|
||||
: ncached;
|
||||
|
||||
void **cur = bin->stack_head;
|
||||
void **limit = cur + max_scan;
|
||||
for (; cur < limit; cur++) {
|
||||
if (*cur == ptr) {
|
||||
safety_check_fail(
|
||||
"Invalid deallocation detected: double free of "
|
||||
"pointer %p\n",
|
||||
ptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an object into the given bin. Fails only if the bin is full.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
cache_bin_dalloc_easy(cache_bin_t *bin, void *ptr) {
|
||||
if (unlikely(cache_bin_full(bin))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unlikely(cache_bin_dalloc_safety_checks(bin, ptr))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bin->stack_head--;
|
||||
*bin->stack_head = ptr;
|
||||
cache_bin_assert_earlier(bin, bin->low_bits_full,
|
||||
(cache_bin_sz_t)(uintptr_t)bin->stack_head);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns false if failed to stash (i.e. bin is full). */
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
cache_bin_stash(cache_bin_t *bin, void *ptr) {
|
||||
if (cache_bin_full(bin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Stash at the full position, in the [full, head) range. */
|
||||
cache_bin_sz_t low_bits_head = (cache_bin_sz_t)(uintptr_t)
|
||||
bin->stack_head;
|
||||
/* Wraparound handled as well. */
|
||||
cache_bin_sz_t diff = cache_bin_diff(
|
||||
bin, bin->low_bits_full, low_bits_head);
|
||||
*(void **)((byte_t *)bin->stack_head - diff) = ptr;
|
||||
|
||||
assert(!cache_bin_full(bin));
|
||||
bin->low_bits_full += sizeof(void *);
|
||||
cache_bin_assert_earlier(bin, bin->low_bits_full, low_bits_head);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get the number of stashed pointers. */
|
||||
JEMALLOC_ALWAYS_INLINE cache_bin_sz_t
|
||||
cache_bin_nstashed_get_internal(cache_bin_t *bin) {
|
||||
cache_bin_sz_t ncached_max = cache_bin_ncached_max_get(bin);
|
||||
cache_bin_sz_t low_bits_low_bound = cache_bin_low_bits_low_bound_get(
|
||||
bin);
|
||||
|
||||
cache_bin_sz_t n = cache_bin_diff(
|
||||
bin, low_bits_low_bound, bin->low_bits_full)
|
||||
/ sizeof(void *);
|
||||
assert(n <= ncached_max);
|
||||
if (config_debug && n != 0) {
|
||||
/* Below are for assertions only. */
|
||||
void **low_bound = cache_bin_low_bound_get(bin);
|
||||
|
||||
assert(
|
||||
(cache_bin_sz_t)(uintptr_t)low_bound == low_bits_low_bound);
|
||||
void *stashed = *(low_bound + n - 1);
|
||||
bool aligned = cache_bin_nonfast_aligned(stashed);
|
||||
#ifdef JEMALLOC_JET
|
||||
/* Allow arbitrary pointers to be stashed in tests. */
|
||||
aligned = true;
|
||||
#endif
|
||||
assert(stashed != NULL && aligned);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE cache_bin_sz_t
|
||||
cache_bin_nstashed_get_local(cache_bin_t *bin) {
|
||||
cache_bin_sz_t n = cache_bin_nstashed_get_internal(bin);
|
||||
assert(n <= cache_bin_ncached_max_get(bin));
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtain a racy view of the number of items currently in the cache bin, in the
|
||||
* presence of possible concurrent modifications.
|
||||
*
|
||||
* Note that this is the only racy function in this header. Any other functions
|
||||
* are assumed to be non-racy. The "racy" term here means accessed from another
|
||||
* thread (that is not the owner of the specific cache bin). This only happens
|
||||
* when gathering stats (read-only). The only change because of the racy
|
||||
* condition is that assertions based on mutable fields are omitted.
|
||||
*
|
||||
* It's important to keep in mind that 'bin->stack_head' and
|
||||
* 'bin->low_bits_full' can be modified concurrently and almost no assertions
|
||||
* about their values can be made.
|
||||
*
|
||||
* This function should not call other utility functions because the racy
|
||||
* condition may cause unexpected / undefined behaviors in unverified utility
|
||||
* functions. Currently, this function calls two utility functions
|
||||
* cache_bin_ncached_max_get and cache_bin_low_bits_low_bound_get because
|
||||
* they help access values that will not be concurrently modified.
|
||||
*/
|
||||
static inline void
|
||||
cache_bin_nitems_get_remote(
|
||||
cache_bin_t *bin, cache_bin_sz_t *ncached, cache_bin_sz_t *nstashed) {
|
||||
/* Racy version of cache_bin_ncached_get_internal. */
|
||||
cache_bin_sz_t diff = bin->low_bits_empty
|
||||
- (cache_bin_sz_t)(uintptr_t)bin->stack_head;
|
||||
cache_bin_sz_t n = diff / sizeof(void *);
|
||||
*ncached = n;
|
||||
|
||||
/* Racy version of cache_bin_nstashed_get_internal. */
|
||||
cache_bin_sz_t low_bits_low_bound = cache_bin_low_bits_low_bound_get(
|
||||
bin);
|
||||
n = (bin->low_bits_full - low_bits_low_bound) / sizeof(void *);
|
||||
*nstashed = n;
|
||||
/*
|
||||
* Note that cannot assert anything regarding ncached_max because
|
||||
* it can be configured on the fly and is thus racy.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* For small bins, used to calculate how many items to fill at a time.
|
||||
* The final nfill is calculated by (ncached_max >> (base - offset)).
|
||||
*/
|
||||
typedef struct cache_bin_fill_ctl_s cache_bin_fill_ctl_t;
|
||||
struct cache_bin_fill_ctl_s {
|
||||
uint8_t base;
|
||||
uint8_t offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* Limit how many items can be flushed in a batch (Which is the upper bound
|
||||
* for the nflush parameter in tcache_bin_flush_impl()).
|
||||
* This is to avoid stack overflow when we do batch edata look up, which
|
||||
* reserves a nflush * sizeof(emap_batch_lookup_result_t) stack variable.
|
||||
*/
|
||||
#define CACHE_BIN_NFLUSH_BATCH_MAX \
|
||||
((VARIABLE_ARRAY_SIZE_MAX >> LG_SIZEOF_PTR) - 1)
|
||||
|
||||
/*
|
||||
* Filling and flushing are done in batch, on arrays of void *s. For filling,
|
||||
* the arrays go forward, and can be accessed with ordinary array arithmetic.
|
||||
* For flushing, we work from the end backwards, and so need to use special
|
||||
* accessors that invert the usual ordering.
|
||||
*
|
||||
* This is important for maintaining first-fit; the arena code fills with
|
||||
* earliest objects first, and so those are the ones we should return first for
|
||||
* cache_bin_alloc calls. When flushing, we should flush the objects that we
|
||||
* wish to return later; those at the end of the array. This is better for the
|
||||
* first-fit heuristic as well as for cache locality; the most recently freed
|
||||
* objects are the ones most likely to still be in cache.
|
||||
*
|
||||
* This all sounds very hand-wavey and theoretical, but reverting the ordering
|
||||
* on one or the other pathway leads to measurable slowdowns.
|
||||
*/
|
||||
|
||||
typedef struct cache_bin_ptr_array_s cache_bin_ptr_array_t;
|
||||
struct cache_bin_ptr_array_s {
|
||||
cache_bin_sz_t n;
|
||||
void **ptr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Declare a cache_bin_ptr_array_t sufficient for nval items.
|
||||
*
|
||||
* In the current implementation, this could be just part of a
|
||||
* cache_bin_ptr_array_init_... call, since we reuse the cache bin stack memory.
|
||||
* Indirecting behind a macro, though, means experimenting with linked-list
|
||||
* representations is easy (since they'll require an alloca in the calling
|
||||
* frame).
|
||||
*/
|
||||
#define CACHE_BIN_PTR_ARRAY_DECLARE(name, nval) \
|
||||
cache_bin_ptr_array_t name; \
|
||||
name.n = (nval)
|
||||
|
||||
/*
|
||||
* Start a fill. The bin must be empty, and This must be followed by a
|
||||
* finish_fill call before doing any alloc/dalloc operations on the bin.
|
||||
*/
|
||||
static inline void
|
||||
cache_bin_init_ptr_array_for_fill(
|
||||
cache_bin_t *bin, cache_bin_ptr_array_t *arr, cache_bin_sz_t nfill) {
|
||||
cache_bin_assert_empty(bin);
|
||||
arr->ptr = cache_bin_empty_position_get(bin) - nfill;
|
||||
}
|
||||
|
||||
/*
|
||||
* While nfill in cache_bin_init_ptr_array_for_fill is the number we *intend* to
|
||||
* fill, nfilled here is the number we actually filled (which may be less, in
|
||||
* case of OOM.
|
||||
*/
|
||||
static inline void
|
||||
cache_bin_finish_fill(
|
||||
cache_bin_t *bin, cache_bin_ptr_array_t *arr, cache_bin_sz_t nfilled) {
|
||||
cache_bin_assert_empty(bin);
|
||||
void **empty_position = cache_bin_empty_position_get(bin);
|
||||
if (nfilled < arr->n) {
|
||||
memmove(empty_position - nfilled, empty_position - arr->n,
|
||||
nfilled * sizeof(void *));
|
||||
}
|
||||
bin->stack_head = empty_position - nfilled;
|
||||
/* Reset the bin stats as it's merged during fill. */
|
||||
if (config_stats) {
|
||||
bin->tstats.nrequests = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Same deal, but with flush. Unlike fill (which can fail), the user must flush
|
||||
* everything we give them.
|
||||
*/
|
||||
static inline void
|
||||
cache_bin_init_ptr_array_for_flush(
|
||||
cache_bin_t *bin, cache_bin_ptr_array_t *arr, cache_bin_sz_t nflush) {
|
||||
arr->ptr = cache_bin_empty_position_get(bin) - nflush;
|
||||
assert(cache_bin_ncached_get_local(bin) == 0 || *arr->ptr != NULL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
cache_bin_finish_flush(
|
||||
cache_bin_t *bin, cache_bin_ptr_array_t *arr, cache_bin_sz_t nflushed) {
|
||||
unsigned rem = cache_bin_ncached_get_local(bin) - nflushed;
|
||||
memmove(
|
||||
bin->stack_head + nflushed, bin->stack_head, rem * sizeof(void *));
|
||||
bin->stack_head += nflushed;
|
||||
cache_bin_low_water_adjust(bin);
|
||||
/* Reset the bin stats as it's merged during flush. */
|
||||
if (config_stats) {
|
||||
bin->tstats.nrequests = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
cache_bin_init_ptr_array_for_stashed(cache_bin_t *bin, szind_t binind,
|
||||
cache_bin_ptr_array_t *arr, cache_bin_sz_t nstashed) {
|
||||
assert(nstashed > 0);
|
||||
assert(cache_bin_nstashed_get_local(bin) == nstashed);
|
||||
|
||||
void **low_bound = cache_bin_low_bound_get(bin);
|
||||
arr->ptr = low_bound;
|
||||
assert(*arr->ptr != NULL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
cache_bin_finish_flush_stashed(cache_bin_t *bin) {
|
||||
void **low_bound = cache_bin_low_bound_get(bin);
|
||||
|
||||
/* Reset the bin local full position. */
|
||||
bin->low_bits_full = (uint16_t)(uintptr_t)low_bound;
|
||||
assert(cache_bin_nstashed_get_local(bin) == 0);
|
||||
/* Reset the bin stats as it's merged during flush. */
|
||||
if (config_stats) {
|
||||
bin->tstats.nrequests = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a cache_bin_info to represent up to the given number of items in
|
||||
* the cache_bins it is associated with.
|
||||
*/
|
||||
void cache_bin_info_init(
|
||||
cache_bin_info_t *bin_info, cache_bin_sz_t ncached_max);
|
||||
/*
|
||||
* Given an array of initialized cache_bin_info_ts, determine how big an
|
||||
* allocation is required to initialize a full set of cache_bin_ts.
|
||||
*/
|
||||
void cache_bin_info_compute_alloc(const cache_bin_info_t *infos, szind_t ninfos,
|
||||
size_t *size, size_t *alignment);
|
||||
|
||||
/*
|
||||
* Actually initialize some cache bins. Callers should allocate the backing
|
||||
* memory indicated by a call to cache_bin_compute_alloc. They should then
|
||||
* preincrement, call init once for each bin and info, and then call
|
||||
* cache_bin_postincrement. *alloc_cur will then point immediately past the end
|
||||
* of the allocation.
|
||||
*/
|
||||
void cache_bin_preincrement(const cache_bin_info_t *infos, szind_t ninfos,
|
||||
void *alloc, size_t *cur_offset);
|
||||
void cache_bin_postincrement(void *alloc, size_t *cur_offset);
|
||||
void cache_bin_init(cache_bin_t *bin, const cache_bin_info_t *info, void *alloc,
|
||||
size_t *cur_offset);
|
||||
void cache_bin_init_disabled(cache_bin_t *bin, cache_bin_sz_t ncached_max);
|
||||
|
||||
bool cache_bin_stack_use_thp(void);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_CACHE_BIN_H */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef JEMALLOC_INTERNAL_CKH_H
|
||||
#define JEMALLOC_INTERNAL_CKH_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/tsd.h"
|
||||
|
||||
/* Cuckoo hashing implementation. Skip to the end for the interface. */
|
||||
|
|
@ -21,8 +22,8 @@
|
|||
#define LG_CKH_BUCKET_CELLS (LG_CACHELINE - LG_SIZEOF_PTR - 1)
|
||||
|
||||
/* Typedefs to allow easy function pointer passing. */
|
||||
typedef void ckh_hash_t (const void *, size_t[2]);
|
||||
typedef bool ckh_keycomp_t (const void *, const void *);
|
||||
typedef void ckh_hash_t(const void *, size_t[2]);
|
||||
typedef bool ckh_keycomp_t(const void *, const void *);
|
||||
|
||||
/* Hash table cell. */
|
||||
typedef struct {
|
||||
|
|
@ -55,7 +56,7 @@ typedef struct {
|
|||
unsigned lg_curbuckets;
|
||||
|
||||
/* Hash and comparison functions. */
|
||||
ckh_hash_t *hash;
|
||||
ckh_hash_t *hash;
|
||||
ckh_keycomp_t *keycomp;
|
||||
|
||||
/* Hash table with 2^lg_curbuckets buckets. */
|
||||
|
|
@ -88,8 +89,8 @@ bool ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data);
|
|||
* the key and value, and doesn't do any lifetime management.
|
||||
*/
|
||||
bool ckh_insert(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data);
|
||||
bool ckh_remove(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key,
|
||||
void **data);
|
||||
bool ckh_remove(
|
||||
tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key, void **data);
|
||||
bool ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data);
|
||||
|
||||
/* Some useful hash and comparison functions for strings and pointers. */
|
||||
|
|
|
|||
23
include/jemalloc/internal/conf.h
Normal file
23
include/jemalloc/internal/conf.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef JEMALLOC_INTERNAL_CONF_H
|
||||
#define JEMALLOC_INTERNAL_CONF_H
|
||||
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
void malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
|
||||
char readlink_buf[PATH_MAX + 1]);
|
||||
void malloc_abort_invalid_conf(void);
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
extern bool had_conf_error;
|
||||
|
||||
bool conf_next(char const **opts_p, char const **k_p, size_t *klen_p,
|
||||
char const **v_p, size_t *vlen_p);
|
||||
void conf_error(
|
||||
const char *msg, const char *k, size_t klen, const char *v, size_t vlen);
|
||||
bool conf_handle_bool(const char *v, size_t vlen, bool *result);
|
||||
bool conf_handle_signed(const char *v, size_t vlen, intmax_t min, intmax_t max,
|
||||
bool check_min, bool check_max, bool clip, intmax_t *result);
|
||||
bool conf_handle_char_p(const char *v, size_t vlen, char *dest, size_t dest_sz);
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_CONF_H */
|
||||
36
include/jemalloc/internal/counter.h
Normal file
36
include/jemalloc/internal/counter.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef JEMALLOC_INTERNAL_COUNTER_H
|
||||
#define JEMALLOC_INTERNAL_COUNTER_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/lockedint.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
|
||||
typedef struct counter_accum_s {
|
||||
LOCKEDINT_MTX_DECLARE(mtx)
|
||||
locked_u64_t accumbytes;
|
||||
uint64_t interval;
|
||||
} counter_accum_t;
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
counter_accum(tsdn_t *tsdn, counter_accum_t *counter, uint64_t bytes) {
|
||||
uint64_t interval = counter->interval;
|
||||
assert(interval > 0);
|
||||
LOCKEDINT_MTX_LOCK(tsdn, counter->mtx);
|
||||
/*
|
||||
* If the event moves fast enough (and/or if the event handling is slow
|
||||
* enough), extreme overflow can cause counter trigger coalescing.
|
||||
* This is an intentional mechanism that avoids rate-limiting
|
||||
* allocation.
|
||||
*/
|
||||
bool overflow = locked_inc_mod_u64(tsdn, LOCKEDINT_MTX(counter->mtx),
|
||||
&counter->accumbytes, bytes, interval);
|
||||
LOCKEDINT_MTX_UNLOCK(tsdn, counter->mtx);
|
||||
return overflow;
|
||||
}
|
||||
|
||||
bool counter_accum_init(counter_accum_t *counter, uint64_t interval);
|
||||
void counter_prefork(tsdn_t *tsdn, counter_accum_t *counter);
|
||||
void counter_postfork_parent(tsdn_t *tsdn, counter_accum_t *counter);
|
||||
void counter_postfork_child(tsdn_t *tsdn, counter_accum_t *counter);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_COUNTER_H */
|
||||
|
|
@ -1,53 +1,64 @@
|
|||
#ifndef JEMALLOC_INTERNAL_CTL_H
|
||||
#define JEMALLOC_INTERNAL_CTL_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_stats.h"
|
||||
#include "jemalloc/internal/background_thread_structs.h"
|
||||
#include "jemalloc/internal/bin_stats.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/malloc_io.h"
|
||||
#include "jemalloc/internal/mutex_prof.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
|
||||
/* Maximum ctl tree depth. */
|
||||
#define CTL_MAX_DEPTH 7
|
||||
#define CTL_MAX_DEPTH 7
|
||||
#define CTL_MULTI_SETTING_MAX_LEN 1000
|
||||
|
||||
typedef struct ctl_node_s {
|
||||
bool named;
|
||||
} ctl_node_t;
|
||||
|
||||
typedef struct ctl_named_node_s {
|
||||
ctl_node_t node;
|
||||
ctl_node_t node;
|
||||
const char *name;
|
||||
/* If (nchildren == 0), this is a terminal node. */
|
||||
size_t nchildren;
|
||||
size_t nchildren;
|
||||
const ctl_node_t *children;
|
||||
int (*ctl)(tsd_t *, const size_t *, size_t, void *, size_t *, void *,
|
||||
size_t);
|
||||
int (*ctl)(
|
||||
tsd_t *, const size_t *, size_t, void *, size_t *, void *, size_t);
|
||||
} ctl_named_node_t;
|
||||
|
||||
typedef struct ctl_indexed_node_s {
|
||||
struct ctl_node_s node;
|
||||
const ctl_named_node_t *(*index)(tsdn_t *, const size_t *, size_t,
|
||||
size_t);
|
||||
const ctl_named_node_t *(*index)(
|
||||
tsdn_t *, const size_t *, size_t, size_t);
|
||||
} ctl_indexed_node_t;
|
||||
|
||||
typedef struct ctl_arena_stats_s {
|
||||
arena_stats_t astats;
|
||||
|
||||
/* Aggregate stats for small size classes, based on bin stats. */
|
||||
size_t allocated_small;
|
||||
size_t allocated_small;
|
||||
uint64_t nmalloc_small;
|
||||
uint64_t ndalloc_small;
|
||||
uint64_t nrequests_small;
|
||||
uint64_t nfills_small;
|
||||
uint64_t nflushes_small;
|
||||
|
||||
bin_stats_t bstats[NBINS];
|
||||
arena_stats_large_t lstats[NSIZES - NBINS];
|
||||
bin_stats_data_t bstats[SC_NBINS];
|
||||
arena_stats_large_t lstats[SC_NSIZES - SC_NBINS];
|
||||
pac_estats_t estats[SC_NPSIZES];
|
||||
hpa_shard_stats_t hpastats;
|
||||
} ctl_arena_stats_t;
|
||||
|
||||
typedef struct ctl_stats_s {
|
||||
size_t allocated;
|
||||
size_t active;
|
||||
size_t metadata;
|
||||
size_t metadata_edata;
|
||||
size_t metadata_rtree;
|
||||
size_t metadata_thp;
|
||||
size_t resident;
|
||||
size_t mapped;
|
||||
|
|
@ -60,17 +71,17 @@ typedef struct ctl_stats_s {
|
|||
typedef struct ctl_arena_s ctl_arena_t;
|
||||
struct ctl_arena_s {
|
||||
unsigned arena_ind;
|
||||
bool initialized;
|
||||
bool initialized;
|
||||
ql_elm(ctl_arena_t) destroyed_link;
|
||||
|
||||
/* Basic stats, supported even if !config_stats. */
|
||||
unsigned nthreads;
|
||||
unsigned nthreads;
|
||||
const char *dss;
|
||||
ssize_t dirty_decay_ms;
|
||||
ssize_t muzzy_decay_ms;
|
||||
size_t pactive;
|
||||
size_t pdirty;
|
||||
size_t pmuzzy;
|
||||
ssize_t dirty_decay_ms;
|
||||
ssize_t muzzy_decay_ms;
|
||||
size_t pactive;
|
||||
size_t pdirty;
|
||||
size_t pmuzzy;
|
||||
|
||||
/* NULL if !config_stats. */
|
||||
ctl_arena_stats_t *astats;
|
||||
|
|
@ -93,39 +104,69 @@ typedef struct ctl_arenas_s {
|
|||
int ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,
|
||||
void *newp, size_t newlen);
|
||||
int ctl_nametomib(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp);
|
||||
|
||||
int ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
|
||||
size_t *oldlenp, void *newp, size_t newlen);
|
||||
int ctl_mibnametomib(
|
||||
tsd_t *tsd, size_t *mib, size_t miblen, const char *name, size_t *miblenp);
|
||||
int ctl_bymibname(tsd_t *tsd, size_t *mib, size_t miblen, const char *name,
|
||||
size_t *miblenp, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
|
||||
bool ctl_boot(void);
|
||||
void ctl_prefork(tsdn_t *tsdn);
|
||||
void ctl_postfork_parent(tsdn_t *tsdn);
|
||||
void ctl_postfork_child(tsdn_t *tsdn);
|
||||
void ctl_mtx_assert_held(tsdn_t *tsdn);
|
||||
|
||||
#define xmallctl(name, oldp, oldlenp, newp, newlen) do { \
|
||||
if (je_mallctl(name, oldp, oldlenp, newp, newlen) \
|
||||
!= 0) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: Failure in xmallctl(\"%s\", ...)\n", \
|
||||
name); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define xmallctl(name, oldp, oldlenp, newp, newlen) \
|
||||
do { \
|
||||
if (je_mallctl(name, oldp, oldlenp, newp, newlen) != 0) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: Failure in xmallctl(\"%s\", ...)\n", \
|
||||
name); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define xmallctlnametomib(name, mibp, miblenp) do { \
|
||||
if (je_mallctlnametomib(name, mibp, miblenp) != 0) { \
|
||||
malloc_printf("<jemalloc>: Failure in " \
|
||||
"xmallctlnametomib(\"%s\", ...)\n", name); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define xmallctlnametomib(name, mibp, miblenp) \
|
||||
do { \
|
||||
if (je_mallctlnametomib(name, mibp, miblenp) != 0) { \
|
||||
malloc_printf( \
|
||||
"<jemalloc>: Failure in " \
|
||||
"xmallctlnametomib(\"%s\", ...)\n", \
|
||||
name); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define xmallctlbymib(mib, miblen, oldp, oldlenp, newp, newlen) do { \
|
||||
if (je_mallctlbymib(mib, miblen, oldp, oldlenp, newp, \
|
||||
newlen) != 0) { \
|
||||
malloc_write( \
|
||||
"<jemalloc>: Failure in xmallctlbymib()\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define xmallctlbymib(mib, miblen, oldp, oldlenp, newp, newlen) \
|
||||
do { \
|
||||
if (je_mallctlbymib(mib, miblen, oldp, oldlenp, newp, newlen) \
|
||||
!= 0) { \
|
||||
malloc_write( \
|
||||
"<jemalloc>: Failure in xmallctlbymib()\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define xmallctlmibnametomib(mib, miblen, name, miblenp) \
|
||||
do { \
|
||||
if (ctl_mibnametomib(tsd_fetch(), mib, miblen, name, miblenp) \
|
||||
!= 0) { \
|
||||
malloc_write( \
|
||||
"<jemalloc>: Failure in ctl_mibnametomib()\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define xmallctlbymibname( \
|
||||
mib, miblen, name, miblenp, oldp, oldlenp, newp, newlen) \
|
||||
do { \
|
||||
if (ctl_bymibname(tsd_fetch(), mib, miblen, name, miblenp, \
|
||||
oldp, oldlenp, newp, newlen) \
|
||||
!= 0) { \
|
||||
malloc_write( \
|
||||
"<jemalloc>: Failure in ctl_bymibname()\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_CTL_H */
|
||||
|
|
|
|||
188
include/jemalloc/internal/decay.h
Normal file
188
include/jemalloc/internal/decay.h
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
#ifndef JEMALLOC_INTERNAL_DECAY_H
|
||||
#define JEMALLOC_INTERNAL_DECAY_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/smoothstep.h"
|
||||
|
||||
#define DECAY_UNBOUNDED_TIME_TO_PURGE ((uint64_t) - 1)
|
||||
|
||||
/*
|
||||
* The decay_t computes the number of pages we should purge at any given time.
|
||||
* Page allocators inform a decay object when pages enter a decay-able state
|
||||
* (i.e. dirty or muzzy), and query it to determine how many pages should be
|
||||
* purged at any given time.
|
||||
*
|
||||
* This is mostly a single-threaded data structure and doesn't care about
|
||||
* synchronization at all; it's the caller's responsibility to manage their
|
||||
* synchronization on their own. There are two exceptions:
|
||||
* 1) It's OK to racily call decay_ms_read (i.e. just the simplest state query).
|
||||
* 2) The mtx and purging fields live (and are initialized) here, but are
|
||||
* logically owned by the page allocator. This is just a convenience (since
|
||||
* those fields would be duplicated for both the dirty and muzzy states
|
||||
* otherwise).
|
||||
*/
|
||||
typedef struct decay_s decay_t;
|
||||
struct decay_s {
|
||||
/* Synchronizes all non-atomic fields. */
|
||||
malloc_mutex_t mtx;
|
||||
/*
|
||||
* True if a thread is currently purging the extents associated with
|
||||
* this decay structure.
|
||||
*/
|
||||
bool purging;
|
||||
/*
|
||||
* Approximate time in milliseconds from the creation of a set of unused
|
||||
* dirty pages until an equivalent set of unused dirty pages is purged
|
||||
* and/or reused.
|
||||
*/
|
||||
atomic_zd_t time_ms;
|
||||
/* time / SMOOTHSTEP_NSTEPS. */
|
||||
nstime_t interval;
|
||||
/*
|
||||
* Time at which the current decay interval logically started. We do
|
||||
* not actually advance to a new epoch until sometime after it starts
|
||||
* because of scheduling and computation delays, and it is even possible
|
||||
* to completely skip epochs. In all cases, during epoch advancement we
|
||||
* merge all relevant activity into the most recently recorded epoch.
|
||||
*/
|
||||
nstime_t epoch;
|
||||
/* Deadline randomness generator. */
|
||||
uint64_t jitter_state;
|
||||
/*
|
||||
* Deadline for current epoch. This is the sum of interval and per
|
||||
* epoch jitter which is a uniform random variable in [0..interval).
|
||||
* Epochs always advance by precise multiples of interval, but we
|
||||
* randomize the deadline to reduce the likelihood of arenas purging in
|
||||
* lockstep.
|
||||
*/
|
||||
nstime_t deadline;
|
||||
/*
|
||||
* The number of pages we cap ourselves at in the current epoch, per
|
||||
* decay policies. Updated on an epoch change. After an epoch change,
|
||||
* the caller should take steps to try to purge down to this amount.
|
||||
*/
|
||||
size_t npages_limit;
|
||||
/*
|
||||
* Number of unpurged pages at beginning of current epoch. During epoch
|
||||
* advancement we use the delta between arena->decay_*.nunpurged and
|
||||
* ecache_npages_get(&arena->ecache_*) to determine how many dirty pages,
|
||||
* if any, were generated.
|
||||
*/
|
||||
size_t nunpurged;
|
||||
/*
|
||||
* Trailing log of how many unused dirty pages were generated during
|
||||
* each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last
|
||||
* element is the most recent epoch. Corresponding epoch times are
|
||||
* relative to epoch.
|
||||
*
|
||||
* Updated only on epoch advance, triggered by
|
||||
* decay_maybe_advance_epoch, below.
|
||||
*/
|
||||
size_t backlog[SMOOTHSTEP_NSTEPS];
|
||||
|
||||
/* Peak number of pages in associated extents. Used for debug only. */
|
||||
uint64_t ceil_npages;
|
||||
};
|
||||
|
||||
/*
|
||||
* The current decay time setting. This is the only public access to a decay_t
|
||||
* that's allowed without holding mtx.
|
||||
*/
|
||||
static inline ssize_t
|
||||
decay_ms_read(const decay_t *decay) {
|
||||
return atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
/*
|
||||
* See the comment on the struct field -- the limit on pages we should allow in
|
||||
* this decay state this epoch.
|
||||
*/
|
||||
static inline size_t
|
||||
decay_npages_limit_get(const decay_t *decay) {
|
||||
return decay->npages_limit;
|
||||
}
|
||||
|
||||
/* How many unused dirty pages were generated during the last epoch. */
|
||||
static inline size_t
|
||||
decay_epoch_npages_delta(const decay_t *decay) {
|
||||
return decay->backlog[SMOOTHSTEP_NSTEPS - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Current epoch duration, in nanoseconds. Given that new epochs are started
|
||||
* somewhat haphazardly, this is not necessarily exactly the time between any
|
||||
* two calls to decay_maybe_advance_epoch; see the comments on fields in the
|
||||
* decay_t.
|
||||
*/
|
||||
static inline uint64_t
|
||||
decay_epoch_duration_ns(const decay_t *decay) {
|
||||
return nstime_ns(&decay->interval);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
decay_immediately(const decay_t *decay) {
|
||||
ssize_t decay_ms = decay_ms_read(decay);
|
||||
return decay_ms == 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
decay_disabled(const decay_t *decay) {
|
||||
ssize_t decay_ms = decay_ms_read(decay);
|
||||
return decay_ms < 0;
|
||||
}
|
||||
|
||||
/* Returns true if decay is enabled and done gradually. */
|
||||
static inline bool
|
||||
decay_gradually(const decay_t *decay) {
|
||||
ssize_t decay_ms = decay_ms_read(decay);
|
||||
return decay_ms > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the passed in decay time setting is valid.
|
||||
* < -1 : invalid
|
||||
* -1 : never decay
|
||||
* 0 : decay immediately
|
||||
* > 0 : some positive decay time, up to a maximum allowed value of
|
||||
* NSTIME_SEC_MAX * 1000, which corresponds to decaying somewhere in the early
|
||||
* 27th century. By that time, we expect to have implemented alternate purging
|
||||
* strategies.
|
||||
*/
|
||||
bool decay_ms_valid(ssize_t decay_ms);
|
||||
|
||||
/*
|
||||
* As a precondition, the decay_t must be zeroed out (as if with memset).
|
||||
*
|
||||
* Returns true on error.
|
||||
*/
|
||||
bool decay_init(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms);
|
||||
|
||||
/*
|
||||
* Given an already-initialized decay_t, reinitialize it with the given decay
|
||||
* time. The decay_t must have previously been initialized (and should not then
|
||||
* be zeroed).
|
||||
*/
|
||||
void decay_reinit(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms);
|
||||
|
||||
/*
|
||||
* Compute how many of 'npages_new' pages we would need to purge in 'time'.
|
||||
*/
|
||||
uint64_t decay_npages_purge_in(
|
||||
decay_t *decay, nstime_t *time, size_t npages_new);
|
||||
|
||||
/* Returns true if the epoch advanced and there are pages to purge. */
|
||||
bool decay_maybe_advance_epoch(
|
||||
decay_t *decay, nstime_t *new_time, size_t current_npages);
|
||||
|
||||
/*
|
||||
* Calculates wait time until a number of pages in the interval
|
||||
* [0.5 * npages_threshold .. 1.5 * npages_threshold] should be purged.
|
||||
*
|
||||
* Returns number of nanoseconds or DECAY_UNBOUNDED_TIME_TO_PURGE in case of
|
||||
* indefinite wait.
|
||||
*/
|
||||
uint64_t decay_ns_until_purge(
|
||||
decay_t *decay, size_t npages_current, uint64_t npages_threshold);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_DECAY_H */
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef JEMALLOC_INTERNAL_DIV_H
|
||||
#define JEMALLOC_INTERNAL_DIV_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
|
||||
/*
|
||||
|
|
|
|||
56
include/jemalloc/internal/ecache.h
Normal file
56
include/jemalloc/internal/ecache.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ECACHE_H
|
||||
#define JEMALLOC_INTERNAL_ECACHE_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/eset.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/san.h"
|
||||
|
||||
typedef struct ecache_s ecache_t;
|
||||
struct ecache_s {
|
||||
malloc_mutex_t mtx;
|
||||
eset_t eset;
|
||||
eset_t guarded_eset;
|
||||
/* All stored extents must be in the same state. */
|
||||
extent_state_t state;
|
||||
/* The index of the ehooks the ecache is associated with. */
|
||||
unsigned ind;
|
||||
/*
|
||||
* If true, delay coalescing until eviction; otherwise coalesce during
|
||||
* deallocation.
|
||||
*/
|
||||
bool delay_coalesce;
|
||||
};
|
||||
|
||||
static inline size_t
|
||||
ecache_npages_get(ecache_t *ecache) {
|
||||
return eset_npages_get(&ecache->eset)
|
||||
+ eset_npages_get(&ecache->guarded_eset);
|
||||
}
|
||||
|
||||
/* Get the number of extents in the given page size index. */
|
||||
static inline size_t
|
||||
ecache_nextents_get(ecache_t *ecache, pszind_t ind) {
|
||||
return eset_nextents_get(&ecache->eset, ind)
|
||||
+ eset_nextents_get(&ecache->guarded_eset, ind);
|
||||
}
|
||||
|
||||
/* Get the sum total bytes of the extents in the given page size index. */
|
||||
static inline size_t
|
||||
ecache_nbytes_get(ecache_t *ecache, pszind_t ind) {
|
||||
return eset_nbytes_get(&ecache->eset, ind)
|
||||
+ eset_nbytes_get(&ecache->guarded_eset, ind);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
ecache_ind_get(ecache_t *ecache) {
|
||||
return ecache->ind;
|
||||
}
|
||||
|
||||
bool ecache_init(tsdn_t *tsdn, ecache_t *ecache, extent_state_t state,
|
||||
unsigned ind, bool delay_coalesce);
|
||||
void ecache_prefork(tsdn_t *tsdn, ecache_t *ecache);
|
||||
void ecache_postfork_parent(tsdn_t *tsdn, ecache_t *ecache);
|
||||
void ecache_postfork_child(tsdn_t *tsdn, ecache_t *ecache);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ECACHE_H */
|
||||
795
include/jemalloc/internal/edata.h
Normal file
795
include/jemalloc/internal/edata.h
Normal file
|
|
@ -0,0 +1,795 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EDATA_H
|
||||
#define JEMALLOC_INTERNAL_EDATA_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/bin_info.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/hpdata.h"
|
||||
#include "jemalloc/internal/nstime.h"
|
||||
#include "jemalloc/internal/ph.h"
|
||||
#include "jemalloc/internal/prof_types.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/slab_data.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
#include "jemalloc/internal/typed_list.h"
|
||||
|
||||
/*
|
||||
* sizeof(edata_t) is 128 bytes on 64-bit architectures. Ensure the alignment
|
||||
* to free up the low bits in the rtree leaf.
|
||||
*/
|
||||
#define EDATA_ALIGNMENT 128
|
||||
|
||||
/*
|
||||
* Defines how many nodes visited when enumerating the heap to search for
|
||||
* qualified extents. More nodes visited may result in better choices at
|
||||
* the cost of longer search time. This size should not exceed 2^16 - 1
|
||||
* because we use uint16_t for accessing the queue needed for enumeration.
|
||||
*/
|
||||
#define ESET_ENUMERATE_MAX_NUM 32
|
||||
|
||||
enum extent_state_e {
|
||||
extent_state_active = 0,
|
||||
extent_state_dirty = 1,
|
||||
extent_state_muzzy = 2,
|
||||
extent_state_retained = 3,
|
||||
extent_state_transition = 4, /* States below are intermediate. */
|
||||
extent_state_merging = 5,
|
||||
extent_state_max = 5 /* Sanity checking only. */
|
||||
};
|
||||
typedef enum extent_state_e extent_state_t;
|
||||
|
||||
enum extent_head_state_e {
|
||||
EXTENT_NOT_HEAD,
|
||||
EXTENT_IS_HEAD /* See comments in ehooks_default_merge_impl(). */
|
||||
};
|
||||
typedef enum extent_head_state_e extent_head_state_t;
|
||||
|
||||
/*
|
||||
* Which implementation of the page allocator interface, (PAI, defined in
|
||||
* pai.h) owns the given extent?
|
||||
*/
|
||||
enum extent_pai_e { EXTENT_PAI_PAC = 0, EXTENT_PAI_HPA = 1 };
|
||||
typedef enum extent_pai_e extent_pai_t;
|
||||
|
||||
struct e_prof_info_s {
|
||||
/* Time when this was allocated. */
|
||||
nstime_t e_prof_alloc_time;
|
||||
/* Allocation request size. */
|
||||
size_t e_prof_alloc_size;
|
||||
/* Points to a prof_tctx_t. */
|
||||
atomic_p_t e_prof_tctx;
|
||||
/*
|
||||
* Points to a prof_recent_t for the allocation; NULL
|
||||
* means the recent allocation record no longer exists.
|
||||
* Protected by prof_recent_alloc_mtx.
|
||||
*/
|
||||
atomic_p_t e_prof_recent_alloc;
|
||||
};
|
||||
typedef struct e_prof_info_s e_prof_info_t;
|
||||
|
||||
/*
|
||||
* The information about a particular edata that lives in an emap. Space is
|
||||
* more precious there (the information, plus the edata pointer, has to live in
|
||||
* a 64-bit word if we want to enable a packed representation.
|
||||
*
|
||||
* There are two things that are special about the information here:
|
||||
* - It's quicker to access. You have one fewer pointer hop, since finding the
|
||||
* edata_t associated with an item always requires accessing the rtree leaf in
|
||||
* which this data is stored.
|
||||
* - It can be read unsynchronized, and without worrying about lifetime issues.
|
||||
*/
|
||||
typedef struct edata_map_info_s edata_map_info_t;
|
||||
struct edata_map_info_s {
|
||||
bool slab;
|
||||
szind_t szind;
|
||||
};
|
||||
|
||||
typedef struct edata_cmp_summary_s edata_cmp_summary_t;
|
||||
struct edata_cmp_summary_s {
|
||||
uint64_t sn;
|
||||
uintptr_t addr;
|
||||
};
|
||||
|
||||
/* Extent (span of pages). Use accessor functions for e_* fields. */
|
||||
typedef struct edata_s edata_t;
|
||||
ph_structs(edata_avail, edata_t, ESET_ENUMERATE_MAX_NUM);
|
||||
ph_structs(edata_heap, edata_t, ESET_ENUMERATE_MAX_NUM);
|
||||
struct edata_s {
|
||||
/*
|
||||
* Bitfield containing several fields:
|
||||
*
|
||||
* a: arena_ind
|
||||
* b: slab
|
||||
* c: committed
|
||||
* p: pai
|
||||
* z: zeroed
|
||||
* g: guarded
|
||||
* t: state
|
||||
* i: szind
|
||||
* f: nfree
|
||||
* s: bin_shard
|
||||
*
|
||||
* 00000000 ... 0000ssss ssffffff ffffiiii iiiitttg zpcbaaaa aaaaaaaa
|
||||
*
|
||||
* arena_ind: Arena from which this extent came, or all 1 bits if
|
||||
* unassociated.
|
||||
*
|
||||
* slab: The slab flag indicates whether the extent is used for a slab
|
||||
* of small regions. This helps differentiate small size classes,
|
||||
* and it indicates whether interior pointers can be looked up via
|
||||
* iealloc().
|
||||
*
|
||||
* committed: The committed flag indicates whether physical memory is
|
||||
* committed to the extent, whether explicitly or implicitly
|
||||
* as on a system that overcommits and satisfies physical
|
||||
* memory needs on demand via soft page faults.
|
||||
*
|
||||
* pai: The pai flag is an extent_pai_t.
|
||||
*
|
||||
* zeroed: The zeroed flag is used by extent recycling code to track
|
||||
* whether memory is zero-filled.
|
||||
*
|
||||
* guarded: The guarded flag is use by the sanitizer to track whether
|
||||
* the extent has page guards around it.
|
||||
*
|
||||
* state: The state flag is an extent_state_t.
|
||||
*
|
||||
* szind: The szind flag indicates usable size class index for
|
||||
* allocations residing in this extent, regardless of whether the
|
||||
* extent is a slab. Extent size and usable size often differ
|
||||
* even for non-slabs, either due to sz_large_pad or promotion of
|
||||
* sampled small regions.
|
||||
*
|
||||
* nfree: Number of free regions in slab.
|
||||
*
|
||||
* bin_shard: the shard of the bin from which this extent came.
|
||||
*/
|
||||
uint64_t e_bits;
|
||||
#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) \
|
||||
((((((uint64_t)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) \
|
||||
<< (CURRENT_FIELD_SHIFT))
|
||||
|
||||
#define EDATA_BITS_ARENA_WIDTH MALLOCX_ARENA_BITS
|
||||
#define EDATA_BITS_ARENA_SHIFT 0
|
||||
#define EDATA_BITS_ARENA_MASK \
|
||||
MASK(EDATA_BITS_ARENA_WIDTH, EDATA_BITS_ARENA_SHIFT)
|
||||
|
||||
#define EDATA_BITS_SLAB_WIDTH 1
|
||||
#define EDATA_BITS_SLAB_SHIFT (EDATA_BITS_ARENA_WIDTH + EDATA_BITS_ARENA_SHIFT)
|
||||
#define EDATA_BITS_SLAB_MASK MASK(EDATA_BITS_SLAB_WIDTH, EDATA_BITS_SLAB_SHIFT)
|
||||
|
||||
#define EDATA_BITS_COMMITTED_WIDTH 1
|
||||
#define EDATA_BITS_COMMITTED_SHIFT \
|
||||
(EDATA_BITS_SLAB_WIDTH + EDATA_BITS_SLAB_SHIFT)
|
||||
#define EDATA_BITS_COMMITTED_MASK \
|
||||
MASK(EDATA_BITS_COMMITTED_WIDTH, EDATA_BITS_COMMITTED_SHIFT)
|
||||
|
||||
#define EDATA_BITS_PAI_WIDTH 1
|
||||
#define EDATA_BITS_PAI_SHIFT \
|
||||
(EDATA_BITS_COMMITTED_WIDTH + EDATA_BITS_COMMITTED_SHIFT)
|
||||
#define EDATA_BITS_PAI_MASK MASK(EDATA_BITS_PAI_WIDTH, EDATA_BITS_PAI_SHIFT)
|
||||
|
||||
#define EDATA_BITS_ZEROED_WIDTH 1
|
||||
#define EDATA_BITS_ZEROED_SHIFT (EDATA_BITS_PAI_WIDTH + EDATA_BITS_PAI_SHIFT)
|
||||
#define EDATA_BITS_ZEROED_MASK \
|
||||
MASK(EDATA_BITS_ZEROED_WIDTH, EDATA_BITS_ZEROED_SHIFT)
|
||||
|
||||
#define EDATA_BITS_GUARDED_WIDTH 1
|
||||
#define EDATA_BITS_GUARDED_SHIFT \
|
||||
(EDATA_BITS_ZEROED_WIDTH + EDATA_BITS_ZEROED_SHIFT)
|
||||
#define EDATA_BITS_GUARDED_MASK \
|
||||
MASK(EDATA_BITS_GUARDED_WIDTH, EDATA_BITS_GUARDED_SHIFT)
|
||||
|
||||
#define EDATA_BITS_STATE_WIDTH 3
|
||||
#define EDATA_BITS_STATE_SHIFT \
|
||||
(EDATA_BITS_GUARDED_WIDTH + EDATA_BITS_GUARDED_SHIFT)
|
||||
#define EDATA_BITS_STATE_MASK \
|
||||
MASK(EDATA_BITS_STATE_WIDTH, EDATA_BITS_STATE_SHIFT)
|
||||
|
||||
#define EDATA_BITS_SZIND_WIDTH LG_CEIL(SC_NSIZES)
|
||||
#define EDATA_BITS_SZIND_SHIFT (EDATA_BITS_STATE_WIDTH + EDATA_BITS_STATE_SHIFT)
|
||||
#define EDATA_BITS_SZIND_MASK \
|
||||
MASK(EDATA_BITS_SZIND_WIDTH, EDATA_BITS_SZIND_SHIFT)
|
||||
|
||||
#define EDATA_BITS_NFREE_WIDTH (SC_LG_SLAB_MAXREGS + 1)
|
||||
#define EDATA_BITS_NFREE_SHIFT (EDATA_BITS_SZIND_WIDTH + EDATA_BITS_SZIND_SHIFT)
|
||||
#define EDATA_BITS_NFREE_MASK \
|
||||
MASK(EDATA_BITS_NFREE_WIDTH, EDATA_BITS_NFREE_SHIFT)
|
||||
|
||||
#define EDATA_BITS_BINSHARD_WIDTH 6
|
||||
#define EDATA_BITS_BINSHARD_SHIFT \
|
||||
(EDATA_BITS_NFREE_WIDTH + EDATA_BITS_NFREE_SHIFT)
|
||||
#define EDATA_BITS_BINSHARD_MASK \
|
||||
MASK(EDATA_BITS_BINSHARD_WIDTH, EDATA_BITS_BINSHARD_SHIFT)
|
||||
|
||||
#define EDATA_BITS_IS_HEAD_WIDTH 1
|
||||
#define EDATA_BITS_IS_HEAD_SHIFT \
|
||||
(EDATA_BITS_BINSHARD_WIDTH + EDATA_BITS_BINSHARD_SHIFT)
|
||||
#define EDATA_BITS_IS_HEAD_MASK \
|
||||
MASK(EDATA_BITS_IS_HEAD_WIDTH, EDATA_BITS_IS_HEAD_SHIFT)
|
||||
|
||||
/* Pointer to the extent that this structure is responsible for. */
|
||||
void *e_addr;
|
||||
|
||||
union {
|
||||
/*
|
||||
* Extent size and serial number associated with the extent
|
||||
* structure (different than the serial number for the extent at
|
||||
* e_addr).
|
||||
*
|
||||
* ssssssss [...] ssssssss ssssnnnn nnnnnnnn
|
||||
*/
|
||||
size_t e_size_esn;
|
||||
#define EDATA_SIZE_MASK ((size_t) ~(PAGE - 1))
|
||||
#define EDATA_ESN_MASK ((size_t)PAGE - 1)
|
||||
/* Base extent size, which may not be a multiple of PAGE. */
|
||||
size_t e_bsize;
|
||||
};
|
||||
|
||||
/*
|
||||
* If this edata is a user allocation from an HPA, it comes out of some
|
||||
* pageslab (we don't yet support hugepage allocations that don't fit
|
||||
* into pageslabs). This tracks it.
|
||||
*/
|
||||
hpdata_t *e_ps;
|
||||
|
||||
/*
|
||||
* Serial number. These are not necessarily unique; splitting an extent
|
||||
* results in two extents with the same serial number.
|
||||
*/
|
||||
uint64_t e_sn;
|
||||
|
||||
union {
|
||||
/*
|
||||
* List linkage used when the edata_t is active; either in
|
||||
* arena's large allocations or bin_t's slabs_full.
|
||||
*/
|
||||
ql_elm(edata_t) ql_link_active;
|
||||
/*
|
||||
* Pairing heap linkage. Used whenever the extent is inactive
|
||||
* (in the page allocators), or when it is active and in
|
||||
* slabs_nonfull, or when the edata_t is unassociated with an
|
||||
* extent and sitting in an edata_cache.
|
||||
*/
|
||||
union {
|
||||
edata_heap_link_t heap_link;
|
||||
edata_avail_link_t avail_link;
|
||||
};
|
||||
};
|
||||
|
||||
union {
|
||||
/*
|
||||
* List linkage used when the extent is inactive:
|
||||
* - Stashed dirty extents
|
||||
* - Ecache LRU functionality.
|
||||
*/
|
||||
ql_elm(edata_t) ql_link_inactive;
|
||||
/* Small region slab metadata. */
|
||||
slab_data_t e_slab_data;
|
||||
|
||||
/* Profiling data, used for large objects. */
|
||||
e_prof_info_t e_prof_info;
|
||||
};
|
||||
};
|
||||
|
||||
TYPED_LIST(edata_list_active, edata_t, ql_link_active)
|
||||
TYPED_LIST(edata_list_inactive, edata_t, ql_link_inactive)
|
||||
|
||||
static inline unsigned
|
||||
edata_arena_ind_get(const edata_t *edata) {
|
||||
unsigned arena_ind = (unsigned)((edata->e_bits & EDATA_BITS_ARENA_MASK)
|
||||
>> EDATA_BITS_ARENA_SHIFT);
|
||||
assert(arena_ind < MALLOCX_ARENA_LIMIT);
|
||||
|
||||
return arena_ind;
|
||||
}
|
||||
|
||||
static inline szind_t
|
||||
edata_szind_get_maybe_invalid(const edata_t *edata) {
|
||||
szind_t szind = (szind_t)((edata->e_bits & EDATA_BITS_SZIND_MASK)
|
||||
>> EDATA_BITS_SZIND_SHIFT);
|
||||
assert(szind <= SC_NSIZES);
|
||||
return szind;
|
||||
}
|
||||
|
||||
static inline szind_t
|
||||
edata_szind_get(const edata_t *edata) {
|
||||
szind_t szind = edata_szind_get_maybe_invalid(edata);
|
||||
assert(szind < SC_NSIZES); /* Never call when "invalid". */
|
||||
return szind;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
edata_usize_get(const edata_t *edata) {
|
||||
assert(edata != NULL);
|
||||
/*
|
||||
* When sz_large_size_classes_disabled() is true, two cases:
|
||||
* 1. if usize_from_ind is not smaller than SC_LARGE_MINCLASS,
|
||||
* usize_from_size is accurate;
|
||||
* 2. otherwise, usize_from_ind is accurate.
|
||||
*
|
||||
* When sz_large_size_classes_disabled() is not true, the two should be the
|
||||
* same when usize_from_ind is not smaller than SC_LARGE_MINCLASS.
|
||||
*
|
||||
* Note sampled small allocs will be promoted. Their extent size is
|
||||
* recorded in edata_size_get(edata), while their szind reflects the
|
||||
* true usize. Thus, usize retrieved here is still accurate for
|
||||
* sampled small allocs.
|
||||
*/
|
||||
szind_t szind = edata_szind_get(edata);
|
||||
#ifdef JEMALLOC_JET
|
||||
/*
|
||||
* Double free is invalid and results in undefined behavior. However,
|
||||
* for double free tests to end gracefully, return an invalid usize
|
||||
* when szind shows the edata is not active, i.e., szind == SC_NSIZES.
|
||||
*/
|
||||
if (unlikely(szind == SC_NSIZES)) {
|
||||
return SC_LARGE_MAXCLASS + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sz_large_size_classes_disabled() || szind < SC_NBINS) {
|
||||
size_t usize_from_ind = sz_index2size(szind);
|
||||
if (!sz_large_size_classes_disabled()
|
||||
&& usize_from_ind >= SC_LARGE_MINCLASS) {
|
||||
size_t size = (edata->e_size_esn & EDATA_SIZE_MASK);
|
||||
assert(size > sz_large_pad);
|
||||
size_t usize_from_size = size - sz_large_pad;
|
||||
assert(usize_from_ind == usize_from_size);
|
||||
}
|
||||
return usize_from_ind;
|
||||
}
|
||||
|
||||
size_t size = (edata->e_size_esn & EDATA_SIZE_MASK);
|
||||
assert(size > sz_large_pad);
|
||||
size_t usize_from_size = size - sz_large_pad;
|
||||
/*
|
||||
* no matter large size classes disabled or not, usize retrieved from
|
||||
* size is not accurate when smaller than SC_LARGE_MINCLASS.
|
||||
*/
|
||||
assert(usize_from_size >= SC_LARGE_MINCLASS);
|
||||
return usize_from_size;
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
edata_binshard_get(const edata_t *edata) {
|
||||
unsigned binshard = (unsigned)((edata->e_bits
|
||||
& EDATA_BITS_BINSHARD_MASK)
|
||||
>> EDATA_BITS_BINSHARD_SHIFT);
|
||||
assert(binshard < bin_infos[edata_szind_get(edata)].n_shards);
|
||||
return binshard;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
edata_sn_get(const edata_t *edata) {
|
||||
return edata->e_sn;
|
||||
}
|
||||
|
||||
static inline extent_state_t
|
||||
edata_state_get(const edata_t *edata) {
|
||||
return (extent_state_t)((edata->e_bits & EDATA_BITS_STATE_MASK)
|
||||
>> EDATA_BITS_STATE_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
edata_guarded_get(const edata_t *edata) {
|
||||
return (bool)((edata->e_bits & EDATA_BITS_GUARDED_MASK)
|
||||
>> EDATA_BITS_GUARDED_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
edata_zeroed_get(const edata_t *edata) {
|
||||
return (bool)((edata->e_bits & EDATA_BITS_ZEROED_MASK)
|
||||
>> EDATA_BITS_ZEROED_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
edata_committed_get(const edata_t *edata) {
|
||||
return (bool)((edata->e_bits & EDATA_BITS_COMMITTED_MASK)
|
||||
>> EDATA_BITS_COMMITTED_SHIFT);
|
||||
}
|
||||
|
||||
static inline extent_pai_t
|
||||
edata_pai_get(const edata_t *edata) {
|
||||
return (extent_pai_t)((edata->e_bits & EDATA_BITS_PAI_MASK)
|
||||
>> EDATA_BITS_PAI_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
edata_slab_get(const edata_t *edata) {
|
||||
return (bool)((edata->e_bits & EDATA_BITS_SLAB_MASK)
|
||||
>> EDATA_BITS_SLAB_SHIFT);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
edata_nfree_get(const edata_t *edata) {
|
||||
assert(edata_slab_get(edata));
|
||||
return (unsigned)((edata->e_bits & EDATA_BITS_NFREE_MASK)
|
||||
>> EDATA_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
edata_base_get(const edata_t *edata) {
|
||||
assert(edata->e_addr == PAGE_ADDR2BASE(edata->e_addr)
|
||||
|| !edata_slab_get(edata));
|
||||
return PAGE_ADDR2BASE(edata->e_addr);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
edata_addr_get(const edata_t *edata) {
|
||||
assert(edata->e_addr == PAGE_ADDR2BASE(edata->e_addr)
|
||||
|| !edata_slab_get(edata));
|
||||
return edata->e_addr;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
edata_size_get(const edata_t *edata) {
|
||||
return (edata->e_size_esn & EDATA_SIZE_MASK);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
edata_esn_get(const edata_t *edata) {
|
||||
return (edata->e_size_esn & EDATA_ESN_MASK);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
edata_bsize_get(const edata_t *edata) {
|
||||
return edata->e_bsize;
|
||||
}
|
||||
|
||||
static inline hpdata_t *
|
||||
edata_ps_get(const edata_t *edata) {
|
||||
assert(edata_pai_get(edata) == EXTENT_PAI_HPA);
|
||||
return edata->e_ps;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
edata_before_get(const edata_t *edata) {
|
||||
return (void *)((byte_t *)edata_base_get(edata) - PAGE);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
edata_last_get(const edata_t *edata) {
|
||||
return (void *)((byte_t *)edata_base_get(edata) + edata_size_get(edata)
|
||||
- PAGE);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
edata_past_get(const edata_t *edata) {
|
||||
return (
|
||||
void *)((byte_t *)edata_base_get(edata) + edata_size_get(edata));
|
||||
}
|
||||
|
||||
static inline slab_data_t *
|
||||
edata_slab_data_get(edata_t *edata) {
|
||||
assert(edata_slab_get(edata));
|
||||
return &edata->e_slab_data;
|
||||
}
|
||||
|
||||
static inline const slab_data_t *
|
||||
edata_slab_data_get_const(const edata_t *edata) {
|
||||
assert(edata_slab_get(edata));
|
||||
return &edata->e_slab_data;
|
||||
}
|
||||
|
||||
static inline prof_tctx_t *
|
||||
edata_prof_tctx_get(const edata_t *edata) {
|
||||
return (prof_tctx_t *)atomic_load_p(
|
||||
&edata->e_prof_info.e_prof_tctx, ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline const nstime_t *
|
||||
edata_prof_alloc_time_get(const edata_t *edata) {
|
||||
return &edata->e_prof_info.e_prof_alloc_time;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
edata_prof_alloc_size_get(const edata_t *edata) {
|
||||
return edata->e_prof_info.e_prof_alloc_size;
|
||||
}
|
||||
|
||||
static inline prof_recent_t *
|
||||
edata_prof_recent_alloc_get_dont_call_directly(const edata_t *edata) {
|
||||
return (prof_recent_t *)atomic_load_p(
|
||||
&edata->e_prof_info.e_prof_recent_alloc, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_arena_ind_set(edata_t *edata, unsigned arena_ind) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_ARENA_MASK)
|
||||
| ((uint64_t)arena_ind << EDATA_BITS_ARENA_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_binshard_set(edata_t *edata, unsigned binshard) {
|
||||
/* The assertion assumes szind is set already. */
|
||||
assert(binshard < bin_infos[edata_szind_get(edata)].n_shards);
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_BINSHARD_MASK)
|
||||
| ((uint64_t)binshard << EDATA_BITS_BINSHARD_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_addr_set(edata_t *edata, void *addr) {
|
||||
edata->e_addr = addr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_size_set(edata_t *edata, size_t size) {
|
||||
assert((size & ~EDATA_SIZE_MASK) == 0);
|
||||
edata->e_size_esn = size | (edata->e_size_esn & ~EDATA_SIZE_MASK);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_esn_set(edata_t *edata, size_t esn) {
|
||||
edata->e_size_esn = (edata->e_size_esn & ~EDATA_ESN_MASK)
|
||||
| (esn & EDATA_ESN_MASK);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_bsize_set(edata_t *edata, size_t bsize) {
|
||||
edata->e_bsize = bsize;
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_ps_set(edata_t *edata, hpdata_t *ps) {
|
||||
assert(edata_pai_get(edata) == EXTENT_PAI_HPA);
|
||||
edata->e_ps = ps;
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_szind_set(edata_t *edata, szind_t szind) {
|
||||
assert(szind <= SC_NSIZES); /* SC_NSIZES means "invalid". */
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_SZIND_MASK)
|
||||
| ((uint64_t)szind << EDATA_BITS_SZIND_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_nfree_set(edata_t *edata, unsigned nfree) {
|
||||
assert(edata_slab_get(edata));
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_NFREE_MASK)
|
||||
| ((uint64_t)nfree << EDATA_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_nfree_binshard_set(edata_t *edata, unsigned nfree, unsigned binshard) {
|
||||
/* The assertion assumes szind is set already. */
|
||||
assert(binshard < bin_infos[edata_szind_get(edata)].n_shards);
|
||||
edata->e_bits = (edata->e_bits
|
||||
& (~EDATA_BITS_NFREE_MASK
|
||||
& ~EDATA_BITS_BINSHARD_MASK))
|
||||
| ((uint64_t)binshard << EDATA_BITS_BINSHARD_SHIFT)
|
||||
| ((uint64_t)nfree << EDATA_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_nfree_inc(edata_t *edata) {
|
||||
assert(edata_slab_get(edata));
|
||||
edata->e_bits += ((uint64_t)1U << EDATA_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_nfree_dec(edata_t *edata) {
|
||||
assert(edata_slab_get(edata));
|
||||
edata->e_bits -= ((uint64_t)1U << EDATA_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_nfree_sub(edata_t *edata, uint64_t n) {
|
||||
assert(edata_slab_get(edata));
|
||||
edata->e_bits -= (n << EDATA_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_sn_set(edata_t *edata, uint64_t sn) {
|
||||
edata->e_sn = sn;
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_state_set(edata_t *edata, extent_state_t state) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_STATE_MASK)
|
||||
| ((uint64_t)state << EDATA_BITS_STATE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_guarded_set(edata_t *edata, bool guarded) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_GUARDED_MASK)
|
||||
| ((uint64_t)guarded << EDATA_BITS_GUARDED_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_zeroed_set(edata_t *edata, bool zeroed) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_ZEROED_MASK)
|
||||
| ((uint64_t)zeroed << EDATA_BITS_ZEROED_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_committed_set(edata_t *edata, bool committed) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_COMMITTED_MASK)
|
||||
| ((uint64_t)committed << EDATA_BITS_COMMITTED_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_pai_set(edata_t *edata, extent_pai_t pai) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_PAI_MASK)
|
||||
| ((uint64_t)pai << EDATA_BITS_PAI_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_slab_set(edata_t *edata, bool slab) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_SLAB_MASK)
|
||||
| ((uint64_t)slab << EDATA_BITS_SLAB_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_prof_tctx_set(edata_t *edata, prof_tctx_t *tctx) {
|
||||
atomic_store_p(&edata->e_prof_info.e_prof_tctx, tctx, ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_prof_alloc_time_set(edata_t *edata, nstime_t *t) {
|
||||
nstime_copy(&edata->e_prof_info.e_prof_alloc_time, t);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_prof_alloc_size_set(edata_t *edata, size_t size) {
|
||||
edata->e_prof_info.e_prof_alloc_size = size;
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_prof_recent_alloc_set_dont_call_directly(
|
||||
edata_t *edata, prof_recent_t *recent_alloc) {
|
||||
atomic_store_p(&edata->e_prof_info.e_prof_recent_alloc, recent_alloc,
|
||||
ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
edata_is_head_get(edata_t *edata) {
|
||||
return (bool)((edata->e_bits & EDATA_BITS_IS_HEAD_MASK)
|
||||
>> EDATA_BITS_IS_HEAD_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_is_head_set(edata_t *edata, bool is_head) {
|
||||
edata->e_bits = (edata->e_bits & ~EDATA_BITS_IS_HEAD_MASK)
|
||||
| ((uint64_t)is_head << EDATA_BITS_IS_HEAD_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
edata_state_in_transition(extent_state_t state) {
|
||||
return state >= extent_state_transition;
|
||||
}
|
||||
|
||||
/*
|
||||
* Because this function is implemented as a sequence of bitfield modifications,
|
||||
* even though each individual bit is properly initialized, we technically read
|
||||
* uninitialized data within it. This is mostly fine, since most callers get
|
||||
* their edatas from zeroing sources, but callers who make stack edata_ts need
|
||||
* to manually zero them.
|
||||
*/
|
||||
static inline void
|
||||
edata_init(edata_t *edata, unsigned arena_ind, void *addr, size_t size,
|
||||
bool slab, szind_t szind, uint64_t sn, extent_state_t state, bool zeroed,
|
||||
bool committed, extent_pai_t pai, extent_head_state_t is_head) {
|
||||
assert(addr == PAGE_ADDR2BASE(addr) || !slab);
|
||||
|
||||
edata_arena_ind_set(edata, arena_ind);
|
||||
edata_addr_set(edata, addr);
|
||||
edata_size_set(edata, size);
|
||||
edata_slab_set(edata, slab);
|
||||
edata_szind_set(edata, szind);
|
||||
edata_sn_set(edata, sn);
|
||||
edata_state_set(edata, state);
|
||||
edata_guarded_set(edata, false);
|
||||
edata_zeroed_set(edata, zeroed);
|
||||
edata_committed_set(edata, committed);
|
||||
edata_pai_set(edata, pai);
|
||||
edata_is_head_set(edata, is_head == EXTENT_IS_HEAD);
|
||||
if (config_prof) {
|
||||
edata_prof_tctx_set(edata, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
edata_binit(
|
||||
edata_t *edata, void *addr, size_t bsize, uint64_t sn, bool reused) {
|
||||
edata_arena_ind_set(edata, (1U << MALLOCX_ARENA_BITS) - 1);
|
||||
edata_addr_set(edata, addr);
|
||||
edata_bsize_set(edata, bsize);
|
||||
edata_slab_set(edata, false);
|
||||
edata_szind_set(edata, SC_NSIZES);
|
||||
edata_sn_set(edata, sn);
|
||||
edata_state_set(edata, extent_state_active);
|
||||
/* See comments in base_edata_is_reused. */
|
||||
edata_guarded_set(edata, reused);
|
||||
edata_zeroed_set(edata, true);
|
||||
edata_committed_set(edata, true);
|
||||
/*
|
||||
* This isn't strictly true, but base allocated extents never get
|
||||
* deallocated and can't be looked up in the emap, but no sense in
|
||||
* wasting a state bit to encode this fact.
|
||||
*/
|
||||
edata_pai_set(edata, EXTENT_PAI_PAC);
|
||||
}
|
||||
|
||||
static inline int
|
||||
edata_esn_comp(const edata_t *a, const edata_t *b) {
|
||||
size_t a_esn = edata_esn_get(a);
|
||||
size_t b_esn = edata_esn_get(b);
|
||||
|
||||
return (a_esn > b_esn) - (a_esn < b_esn);
|
||||
}
|
||||
|
||||
static inline int
|
||||
edata_ead_comp(const edata_t *a, const edata_t *b) {
|
||||
uintptr_t a_eaddr = (uintptr_t)a;
|
||||
uintptr_t b_eaddr = (uintptr_t)b;
|
||||
|
||||
return (a_eaddr > b_eaddr) - (a_eaddr < b_eaddr);
|
||||
}
|
||||
|
||||
static inline edata_cmp_summary_t
|
||||
edata_cmp_summary_get(const edata_t *edata) {
|
||||
edata_cmp_summary_t result;
|
||||
result.sn = edata_sn_get(edata);
|
||||
result.addr = (uintptr_t)edata_addr_get(edata);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_HAVE_INT128
|
||||
JEMALLOC_ALWAYS_INLINE unsigned __int128
|
||||
edata_cmp_summary_encode(edata_cmp_summary_t src) {
|
||||
return ((unsigned __int128)src.sn << 64) | src.addr;
|
||||
}
|
||||
|
||||
static inline int
|
||||
edata_cmp_summary_comp(edata_cmp_summary_t a, edata_cmp_summary_t b) {
|
||||
unsigned __int128 a_encoded = edata_cmp_summary_encode(a);
|
||||
unsigned __int128 b_encoded = edata_cmp_summary_encode(b);
|
||||
if (a_encoded < b_encoded)
|
||||
return -1;
|
||||
if (a_encoded == b_encoded)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
static inline int
|
||||
edata_cmp_summary_comp(edata_cmp_summary_t a, edata_cmp_summary_t b) {
|
||||
/*
|
||||
* Logically, what we're doing here is comparing based on `.sn`, and
|
||||
* falling back to comparing on `.addr` in the case that `a.sn == b.sn`.
|
||||
* We accomplish this by multiplying the result of the `.sn` comparison
|
||||
* by 2, so that so long as it is not 0, it will dominate the `.addr`
|
||||
* comparison in determining the sign of the returned result value.
|
||||
* The justification for doing things this way is that this is
|
||||
* branchless - all of the branches that would be present in a
|
||||
* straightforward implementation are common cases, and thus the branch
|
||||
* prediction accuracy is not great. As a result, this implementation
|
||||
* is measurably faster (by around 30%).
|
||||
*/
|
||||
return (2 * ((a.sn > b.sn) - (a.sn < b.sn)))
|
||||
+ ((a.addr > b.addr) - (a.addr < b.addr));
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
edata_snad_comp(const edata_t *a, const edata_t *b) {
|
||||
edata_cmp_summary_t a_cmp = edata_cmp_summary_get(a);
|
||||
edata_cmp_summary_t b_cmp = edata_cmp_summary_get(b);
|
||||
|
||||
return edata_cmp_summary_comp(a_cmp, b_cmp);
|
||||
}
|
||||
|
||||
static inline int
|
||||
edata_esnead_comp(const edata_t *a, const edata_t *b) {
|
||||
/*
|
||||
* Similar to `edata_cmp_summary_comp`, we've opted for a
|
||||
* branchless implementation for the sake of performance.
|
||||
*/
|
||||
return (2 * edata_esn_comp(a, b)) + edata_ead_comp(a, b);
|
||||
}
|
||||
|
||||
ph_proto(, edata_avail, edata_t) ph_proto(, edata_heap, edata_t)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EDATA_H */
|
||||
50
include/jemalloc/internal/edata_cache.h
Normal file
50
include/jemalloc/internal/edata_cache.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EDATA_CACHE_H
|
||||
#define JEMALLOC_INTERNAL_EDATA_CACHE_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/base.h"
|
||||
|
||||
/* For tests only. */
|
||||
#define EDATA_CACHE_FAST_FILL 4
|
||||
|
||||
/*
|
||||
* A cache of edata_t structures allocated via base_alloc_edata (as opposed to
|
||||
* the underlying extents they describe). The contents of returned edata_t
|
||||
* objects are garbage and cannot be relied upon.
|
||||
*/
|
||||
|
||||
typedef struct edata_cache_s edata_cache_t;
|
||||
struct edata_cache_s {
|
||||
edata_avail_t avail;
|
||||
atomic_zu_t count;
|
||||
malloc_mutex_t mtx;
|
||||
base_t *base;
|
||||
};
|
||||
|
||||
bool edata_cache_init(edata_cache_t *edata_cache, base_t *base);
|
||||
edata_t *edata_cache_get(tsdn_t *tsdn, edata_cache_t *edata_cache);
|
||||
void edata_cache_put(tsdn_t *tsdn, edata_cache_t *edata_cache, edata_t *edata);
|
||||
|
||||
void edata_cache_prefork(tsdn_t *tsdn, edata_cache_t *edata_cache);
|
||||
void edata_cache_postfork_parent(tsdn_t *tsdn, edata_cache_t *edata_cache);
|
||||
void edata_cache_postfork_child(tsdn_t *tsdn, edata_cache_t *edata_cache);
|
||||
|
||||
/*
|
||||
* An edata_cache_small is like an edata_cache, but it relies on external
|
||||
* synchronization and avoids first-fit strategies.
|
||||
*/
|
||||
|
||||
typedef struct edata_cache_fast_s edata_cache_fast_t;
|
||||
struct edata_cache_fast_s {
|
||||
edata_list_inactive_t list;
|
||||
edata_cache_t *fallback;
|
||||
bool disabled;
|
||||
};
|
||||
|
||||
void edata_cache_fast_init(edata_cache_fast_t *ecs, edata_cache_t *fallback);
|
||||
edata_t *edata_cache_fast_get(tsdn_t *tsdn, edata_cache_fast_t *ecs);
|
||||
void edata_cache_fast_put(
|
||||
tsdn_t *tsdn, edata_cache_fast_t *ecs, edata_t *edata);
|
||||
void edata_cache_fast_disable(tsdn_t *tsdn, edata_cache_fast_t *ecs);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EDATA_CACHE_H */
|
||||
414
include/jemalloc/internal/ehooks.h
Normal file
414
include/jemalloc/internal/ehooks.h
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EHOOKS_H
|
||||
#define JEMALLOC_INTERNAL_EHOOKS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/extent_mmap.h"
|
||||
#include "jemalloc/internal/tsd.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
/*
|
||||
* This module is the internal interface to the extent hooks (both
|
||||
* user-specified and external). Eventually, this will give us the flexibility
|
||||
* to use multiple different versions of user-visible extent-hook APIs under a
|
||||
* single user interface.
|
||||
*
|
||||
* Current API expansions (not available to anyone but the default hooks yet):
|
||||
* - Head state tracking. Hooks can decide whether or not to merge two
|
||||
* extents based on whether or not one of them is the head (i.e. was
|
||||
* allocated on its own). The later extent loses its "head" status.
|
||||
*/
|
||||
|
||||
extern const extent_hooks_t ehooks_default_extent_hooks;
|
||||
|
||||
typedef struct ehooks_s ehooks_t;
|
||||
struct ehooks_s {
|
||||
/*
|
||||
* The user-visible id that goes with the ehooks (i.e. that of the base
|
||||
* they're a part of, the associated arena's index within the arenas
|
||||
* array).
|
||||
*/
|
||||
unsigned ind;
|
||||
/* Logically an extent_hooks_t *. */
|
||||
atomic_p_t ptr;
|
||||
};
|
||||
|
||||
extern const extent_hooks_t ehooks_default_extent_hooks;
|
||||
|
||||
/*
|
||||
* These are not really part of the public API. Each hook has a fast-path for
|
||||
* the default-hooks case that can avoid various small inefficiencies:
|
||||
* - Forgetting tsd and then calling tsd_get within the hook.
|
||||
* - Getting more state than necessary out of the extent_t.
|
||||
* - Doing arena_ind -> arena -> arena_ind lookups.
|
||||
* By making the calls to these functions visible to the compiler, it can move
|
||||
* those extra bits of computation down below the fast-paths where they get ignored.
|
||||
*/
|
||||
void *ehooks_default_alloc_impl(tsdn_t *tsdn, void *new_addr, size_t size,
|
||||
size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
|
||||
bool ehooks_default_dalloc_impl(void *addr, size_t size);
|
||||
void ehooks_default_destroy_impl(void *addr, size_t size);
|
||||
bool ehooks_default_commit_impl(void *addr, size_t offset, size_t length);
|
||||
bool ehooks_default_decommit_impl(void *addr, size_t offset, size_t length);
|
||||
#ifdef PAGES_CAN_PURGE_LAZY
|
||||
bool ehooks_default_purge_lazy_impl(void *addr, size_t offset, size_t length);
|
||||
#endif
|
||||
#ifdef PAGES_CAN_PURGE_FORCED
|
||||
bool ehooks_default_purge_forced_impl(void *addr, size_t offset, size_t length);
|
||||
#endif
|
||||
bool ehooks_default_split_impl(void);
|
||||
/*
|
||||
* Merge is the only default extent hook we declare -- see the comment in
|
||||
* ehooks_merge.
|
||||
*/
|
||||
bool ehooks_default_merge(extent_hooks_t *extent_hooks, void *addr_a,
|
||||
size_t size_a, void *addr_b, size_t size_b, bool committed,
|
||||
unsigned arena_ind);
|
||||
bool ehooks_default_merge_impl(tsdn_t *tsdn, void *addr_a, void *addr_b);
|
||||
void ehooks_default_zero_impl(void *addr, size_t size);
|
||||
void ehooks_default_guard_impl(void *guard1, void *guard2);
|
||||
void ehooks_default_unguard_impl(void *guard1, void *guard2);
|
||||
|
||||
/*
|
||||
* We don't officially support reentrancy from wtihin the extent hooks. But
|
||||
* various people who sit within throwing distance of the jemalloc team want
|
||||
* that functionality in certain limited cases. The default reentrancy guards
|
||||
* assert that we're not reentrant from a0 (since it's the bootstrap arena,
|
||||
* where reentrant allocations would be redirected), which we would incorrectly
|
||||
* trigger in cases where a0 has extent hooks (those hooks themselves can't be
|
||||
* reentrant, then, but there are reasonable uses for such functionality, like
|
||||
* putting internal metadata on hugepages). Therefore, we use the raw
|
||||
* reentrancy guards.
|
||||
*
|
||||
* Eventually, we need to think more carefully about whether and where we
|
||||
* support allocating from within extent hooks (and what that means for things
|
||||
* like profiling, stats collection, etc.), and document what the guarantee is.
|
||||
*/
|
||||
static inline void
|
||||
ehooks_pre_reentrancy(tsdn_t *tsdn) {
|
||||
tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
|
||||
tsd_pre_reentrancy_raw(tsd);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_post_reentrancy(tsdn_t *tsdn) {
|
||||
tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
|
||||
tsd_post_reentrancy_raw(tsd);
|
||||
}
|
||||
|
||||
/* Beginning of the public API. */
|
||||
void ehooks_init(ehooks_t *ehooks, extent_hooks_t *extent_hooks, unsigned ind);
|
||||
|
||||
static inline unsigned
|
||||
ehooks_ind_get(const ehooks_t *ehooks) {
|
||||
return ehooks->ind;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_set_extent_hooks_ptr(ehooks_t *ehooks, extent_hooks_t *extent_hooks) {
|
||||
atomic_store_p(&ehooks->ptr, extent_hooks, ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
static inline extent_hooks_t *
|
||||
ehooks_get_extent_hooks_ptr(ehooks_t *ehooks) {
|
||||
return (extent_hooks_t *)atomic_load_p(&ehooks->ptr, ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_are_default(ehooks_t *ehooks) {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks)
|
||||
== &ehooks_default_extent_hooks;
|
||||
}
|
||||
|
||||
/*
|
||||
* In some cases, a caller needs to allocate resources before attempting to call
|
||||
* a hook. If that hook is doomed to fail, this is wasteful. We therefore
|
||||
* include some checks for such cases.
|
||||
*/
|
||||
static inline bool
|
||||
ehooks_dalloc_will_fail(ehooks_t *ehooks) {
|
||||
if (ehooks_are_default(ehooks)) {
|
||||
return opt_retain;
|
||||
} else {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks)->dalloc == NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_split_will_fail(ehooks_t *ehooks) {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks)->split == NULL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_merge_will_fail(ehooks_t *ehooks) {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks)->merge == NULL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_guard_will_fail(ehooks_t *ehooks) {
|
||||
/*
|
||||
* Before the guard hooks are officially introduced, limit the use to
|
||||
* the default hooks only.
|
||||
*/
|
||||
return !ehooks_are_default(ehooks);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some hooks are required to return zeroed memory in certain situations. In
|
||||
* debug mode, we do some heuristic checks that they did what they were supposed
|
||||
* to.
|
||||
*
|
||||
* This isn't really ehooks-specific (i.e. anyone can check for zeroed memory).
|
||||
* But incorrect zero information indicates an ehook bug.
|
||||
*/
|
||||
static inline void
|
||||
ehooks_debug_zero_check(void *addr, size_t size) {
|
||||
assert(((uintptr_t)addr & PAGE_MASK) == 0);
|
||||
assert((size & PAGE_MASK) == 0);
|
||||
assert(size > 0);
|
||||
if (config_debug) {
|
||||
/* Check the whole first page. */
|
||||
size_t *p = (size_t *)addr;
|
||||
for (size_t i = 0; i < PAGE / sizeof(size_t); i++) {
|
||||
assert(p[i] == 0);
|
||||
}
|
||||
/*
|
||||
* And 4 spots within. There's a tradeoff here; the larger
|
||||
* this number, the more likely it is that we'll catch a bug
|
||||
* where ehooks return a sparsely non-zero range. But
|
||||
* increasing the number of checks also increases the number of
|
||||
* page faults in debug mode. FreeBSD does much of their
|
||||
* day-to-day development work in debug mode, so we don't want
|
||||
* even the debug builds to be too slow.
|
||||
*/
|
||||
const size_t nchecks = 4;
|
||||
assert(PAGE >= sizeof(size_t) * nchecks);
|
||||
for (size_t i = 0; i < nchecks; ++i) {
|
||||
assert(p[i * (size / sizeof(size_t) / nchecks)] == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void *
|
||||
ehooks_alloc(tsdn_t *tsdn, ehooks_t *ehooks, void *new_addr, size_t size,
|
||||
size_t alignment, bool *zero, bool *commit) {
|
||||
bool orig_zero = *zero;
|
||||
void *ret;
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ret = ehooks_default_alloc_impl(tsdn, new_addr, size, alignment,
|
||||
zero, commit, ehooks_ind_get(ehooks));
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
ret = extent_hooks->alloc(extent_hooks, new_addr, size,
|
||||
alignment, zero, commit, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
}
|
||||
assert(new_addr == NULL || ret == NULL || new_addr == ret);
|
||||
assert(!orig_zero || *zero);
|
||||
if (*zero && ret != NULL) {
|
||||
ehooks_debug_zero_check(ret, size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_dalloc(
|
||||
tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size, bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_dalloc_impl(addr, size);
|
||||
} else if (extent_hooks->dalloc == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->dalloc(extent_hooks, addr, size,
|
||||
committed, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_destroy(
|
||||
tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size, bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_destroy_impl(addr, size);
|
||||
} else if (extent_hooks->destroy == NULL) {
|
||||
/* Do nothing. */
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
extent_hooks->destroy(extent_hooks, addr, size, committed,
|
||||
ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_commit(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
bool err;
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
err = ehooks_default_commit_impl(addr, offset, length);
|
||||
} else if (extent_hooks->commit == NULL) {
|
||||
err = true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
err = extent_hooks->commit(extent_hooks, addr, size, offset,
|
||||
length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
}
|
||||
if (!err) {
|
||||
ehooks_debug_zero_check(addr, size);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_decommit(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_decommit_impl(addr, offset, length);
|
||||
} else if (extent_hooks->decommit == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->decommit(extent_hooks, addr, size,
|
||||
offset, length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_purge_lazy(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
#ifdef PAGES_CAN_PURGE_LAZY
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_purge_lazy_impl(addr, offset, length);
|
||||
}
|
||||
#endif
|
||||
if (extent_hooks->purge_lazy == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->purge_lazy(extent_hooks, addr, size,
|
||||
offset, length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_purge_forced(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
/*
|
||||
* It would be correct to have a ehooks_debug_zero_check call at the end
|
||||
* of this function; purge_forced is required to zero. But checking
|
||||
* would touch the page in question, which may have performance
|
||||
* consequences (imagine the hooks are using hugepages, with a global
|
||||
* zero page off). Even in debug mode, it's usually a good idea to
|
||||
* avoid cases that can dramatically increase memory consumption.
|
||||
*/
|
||||
#ifdef PAGES_CAN_PURGE_FORCED
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_purge_forced_impl(addr, offset, length);
|
||||
}
|
||||
#endif
|
||||
if (extent_hooks->purge_forced == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->purge_forced(extent_hooks, addr, size,
|
||||
offset, length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_split(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t size_a, size_t size_b, bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (ehooks_are_default(ehooks)) {
|
||||
return ehooks_default_split_impl();
|
||||
} else if (extent_hooks->split == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->split(extent_hooks, addr, size, size_a,
|
||||
size_b, committed, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_merge(tsdn_t *tsdn, ehooks_t *ehooks, void *addr_a, size_t size_a,
|
||||
void *addr_b, size_t size_b, bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_merge_impl(tsdn, addr_a, addr_b);
|
||||
} else if (extent_hooks->merge == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->merge(extent_hooks, addr_a, size_a,
|
||||
addr_b, size_b, committed, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_zero(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_zero_impl(addr, size);
|
||||
} else {
|
||||
/*
|
||||
* It would be correct to try using the user-provided purge
|
||||
* hooks (since they are required to have zeroed the extent if
|
||||
* they indicate success), but we don't necessarily know their
|
||||
* cost. We'll be conservative and use memset.
|
||||
*/
|
||||
memset(addr, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_guard(tsdn_t *tsdn, ehooks_t *ehooks, void *guard1, void *guard2) {
|
||||
bool err;
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_guard_impl(guard1, guard2);
|
||||
err = false;
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_unguard(tsdn_t *tsdn, ehooks_t *ehooks, void *guard1, void *guard2) {
|
||||
bool err;
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_unguard_impl(guard1, guard2);
|
||||
err = false;
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EHOOKS_H */
|
||||
397
include/jemalloc/internal/emap.h
Normal file
397
include/jemalloc/internal/emap.h
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EMAP_H
|
||||
#define JEMALLOC_INTERNAL_EMAP_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/base.h"
|
||||
#include "jemalloc/internal/rtree.h"
|
||||
|
||||
/*
|
||||
* Note: Ends without at semicolon, so that
|
||||
* EMAP_DECLARE_RTREE_CTX;
|
||||
* in uses will avoid empty-statement warnings.
|
||||
*/
|
||||
#define EMAP_DECLARE_RTREE_CTX \
|
||||
rtree_ctx_t rtree_ctx_fallback; \
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback)
|
||||
|
||||
typedef struct emap_s emap_t;
|
||||
struct emap_s {
|
||||
rtree_t rtree;
|
||||
};
|
||||
|
||||
/* Used to pass rtree lookup context down the path. */
|
||||
typedef struct emap_alloc_ctx_s emap_alloc_ctx_t;
|
||||
struct emap_alloc_ctx_s {
|
||||
size_t usize;
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
};
|
||||
|
||||
typedef struct emap_full_alloc_ctx_s emap_full_alloc_ctx_t;
|
||||
struct emap_full_alloc_ctx_s {
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
edata_t *edata;
|
||||
};
|
||||
|
||||
bool emap_init(emap_t *emap, base_t *base, bool zeroed);
|
||||
|
||||
void emap_remap(
|
||||
tsdn_t *tsdn, emap_t *emap, edata_t *edata, szind_t szind, bool slab);
|
||||
|
||||
void emap_update_edata_state(
|
||||
tsdn_t *tsdn, emap_t *emap, edata_t *edata, extent_state_t state);
|
||||
|
||||
/*
|
||||
* The two acquire functions below allow accessing neighbor edatas, if it's safe
|
||||
* and valid to do so (i.e. from the same arena, of the same state, etc.). This
|
||||
* is necessary because the ecache locks are state based, and only protect
|
||||
* edatas with the same state. Therefore the neighbor edata's state needs to be
|
||||
* verified first, before chasing the edata pointer. The returned edata will be
|
||||
* in an acquired state, meaning other threads will be prevented from accessing
|
||||
* it, even if technically the edata can still be discovered from the rtree.
|
||||
*
|
||||
* This means, at any moment when holding pointers to edata, either one of the
|
||||
* state based locks is held (and the edatas are all of the protected state), or
|
||||
* the edatas are in an acquired state (e.g. in active or merging state). The
|
||||
* acquire operation itself (changing the edata to an acquired state) is done
|
||||
* under the state locks.
|
||||
*/
|
||||
edata_t *emap_try_acquire_edata_neighbor(tsdn_t *tsdn, emap_t *emap,
|
||||
edata_t *edata, extent_pai_t pai, extent_state_t expected_state,
|
||||
bool forward);
|
||||
edata_t *emap_try_acquire_edata_neighbor_expand(tsdn_t *tsdn, emap_t *emap,
|
||||
edata_t *edata, extent_pai_t pai, extent_state_t expected_state);
|
||||
void emap_release_edata(
|
||||
tsdn_t *tsdn, emap_t *emap, edata_t *edata, extent_state_t new_state);
|
||||
|
||||
/*
|
||||
* Associate the given edata with its beginning and end address, setting the
|
||||
* szind and slab info appropriately.
|
||||
* Returns true on error (i.e. resource exhaustion).
|
||||
*/
|
||||
bool emap_register_boundary(
|
||||
tsdn_t *tsdn, emap_t *emap, edata_t *edata, szind_t szind, bool slab);
|
||||
|
||||
/*
|
||||
* Does the same thing, but with the interior of the range, for slab
|
||||
* allocations.
|
||||
*
|
||||
* You might wonder why we don't just have a single emap_register function that
|
||||
* does both depending on the value of 'slab'. The answer is twofold:
|
||||
* - As a practical matter, in places like the extract->split->commit pathway,
|
||||
* we defer the interior operation until we're sure that the commit won't fail
|
||||
* (but we have to register the split boundaries there).
|
||||
* - In general, we're trying to move to a world where the page-specific
|
||||
* allocator doesn't know as much about how the pages it allocates will be
|
||||
* used, and passing a 'slab' parameter everywhere makes that more
|
||||
* complicated.
|
||||
*
|
||||
* Unlike the boundary version, this function can't fail; this is because slabs
|
||||
* can't get big enough to touch a new page that neither of the boundaries
|
||||
* touched, so no allocation is necessary to fill the interior once the boundary
|
||||
* has been touched.
|
||||
*/
|
||||
void emap_register_interior(
|
||||
tsdn_t *tsdn, emap_t *emap, edata_t *edata, szind_t szind);
|
||||
|
||||
void emap_deregister_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
|
||||
void emap_deregister_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
|
||||
|
||||
typedef struct emap_prepare_s emap_prepare_t;
|
||||
struct emap_prepare_s {
|
||||
rtree_leaf_elm_t *lead_elm_a;
|
||||
rtree_leaf_elm_t *lead_elm_b;
|
||||
rtree_leaf_elm_t *trail_elm_a;
|
||||
rtree_leaf_elm_t *trail_elm_b;
|
||||
};
|
||||
|
||||
/**
|
||||
* These functions the emap metadata management for merging, splitting, and
|
||||
* reusing extents. In particular, they set the boundary mappings from
|
||||
* addresses to edatas. If the result is going to be used as a slab, you
|
||||
* still need to call emap_register_interior on it, though.
|
||||
*
|
||||
* Remap simply changes the szind and slab status of an extent's boundary
|
||||
* mappings. If the extent is not a slab, it doesn't bother with updating the
|
||||
* end mapping (since lookups only occur in the interior of an extent for
|
||||
* slabs). Since the szind and slab status only make sense for active extents,
|
||||
* this should only be called while activating or deactivating an extent.
|
||||
*
|
||||
* Split and merge have a "prepare" and a "commit" portion. The prepare portion
|
||||
* does the operations that can be done without exclusive access to the extent
|
||||
* in question, while the commit variant requires exclusive access to maintain
|
||||
* the emap invariants. The only function that can fail is emap_split_prepare,
|
||||
* and it returns true on failure (at which point the caller shouldn't commit).
|
||||
*
|
||||
* In all cases, "lead" refers to the lower-addressed extent, and trail to the
|
||||
* higher-addressed one. It's the caller's responsibility to set the edata
|
||||
* state appropriately.
|
||||
*/
|
||||
bool emap_split_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
|
||||
edata_t *edata, size_t size_a, edata_t *trail, size_t size_b);
|
||||
void emap_split_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
|
||||
edata_t *lead, size_t size_a, edata_t *trail, size_t size_b);
|
||||
void emap_merge_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
|
||||
edata_t *lead, edata_t *trail);
|
||||
void emap_merge_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
|
||||
edata_t *lead, edata_t *trail);
|
||||
|
||||
/* Assert that the emap's view of the given edata matches the edata's view. */
|
||||
void emap_do_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
|
||||
static inline void
|
||||
emap_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
|
||||
if (config_debug) {
|
||||
emap_do_assert_mapped(tsdn, emap, edata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Assert that the given edata isn't in the map. */
|
||||
void emap_do_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
|
||||
static inline void
|
||||
emap_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
|
||||
if (config_debug) {
|
||||
emap_do_assert_not_mapped(tsdn, emap, edata);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
emap_edata_in_transition(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
|
||||
assert(config_debug);
|
||||
emap_assert_mapped(tsdn, emap, edata);
|
||||
|
||||
EMAP_DECLARE_RTREE_CTX;
|
||||
rtree_contents_t contents = rtree_read(
|
||||
tsdn, &emap->rtree, rtree_ctx, (uintptr_t)edata_base_get(edata));
|
||||
|
||||
return edata_state_in_transition(contents.metadata.state);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
emap_edata_is_acquired(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
|
||||
if (!config_debug) {
|
||||
/* For assertions only. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* The edata is considered acquired if no other threads will attempt to
|
||||
* read / write any fields from it. This includes a few cases:
|
||||
*
|
||||
* 1) edata not hooked into emap yet -- This implies the edata just got
|
||||
* allocated or initialized.
|
||||
*
|
||||
* 2) in an active or transition state -- In both cases, the edata can
|
||||
* be discovered from the emap, however the state tracked in the rtree
|
||||
* will prevent other threads from accessing the actual edata.
|
||||
*/
|
||||
EMAP_DECLARE_RTREE_CTX;
|
||||
rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &emap->rtree,
|
||||
rtree_ctx, (uintptr_t)edata_base_get(edata), /* dependent */ false,
|
||||
/* init_missing */ false);
|
||||
if (elm == NULL) {
|
||||
return true;
|
||||
}
|
||||
rtree_contents_t contents = rtree_leaf_elm_read(tsdn, &emap->rtree, elm,
|
||||
/* dependent */ false);
|
||||
if (contents.edata == NULL
|
||||
|| contents.metadata.state == extent_state_active
|
||||
|| edata_state_in_transition(contents.metadata.state)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
extent_assert_can_coalesce(const edata_t *inner, const edata_t *outer) {
|
||||
assert(edata_arena_ind_get(inner) == edata_arena_ind_get(outer));
|
||||
assert(edata_pai_get(inner) == edata_pai_get(outer));
|
||||
assert(edata_committed_get(inner) == edata_committed_get(outer));
|
||||
assert(edata_state_get(inner) == extent_state_active);
|
||||
assert(edata_state_get(outer) == extent_state_merging);
|
||||
assert(!edata_guarded_get(inner) && !edata_guarded_get(outer));
|
||||
assert(edata_base_get(inner) == edata_past_get(outer)
|
||||
|| edata_base_get(outer) == edata_past_get(inner));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
extent_assert_can_expand(const edata_t *original, const edata_t *expand) {
|
||||
assert(edata_arena_ind_get(original) == edata_arena_ind_get(expand));
|
||||
assert(edata_pai_get(original) == edata_pai_get(expand));
|
||||
assert(edata_state_get(original) == extent_state_active);
|
||||
assert(edata_state_get(expand) == extent_state_merging);
|
||||
assert(edata_past_get(original) == edata_base_get(expand));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE edata_t *
|
||||
emap_edata_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr) {
|
||||
EMAP_DECLARE_RTREE_CTX;
|
||||
|
||||
return rtree_read(tsdn, &emap->rtree, rtree_ctx, (uintptr_t)ptr).edata;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
emap_alloc_ctx_init(
|
||||
emap_alloc_ctx_t *alloc_ctx, szind_t szind, bool slab, size_t usize) {
|
||||
alloc_ctx->szind = szind;
|
||||
alloc_ctx->slab = slab;
|
||||
alloc_ctx->usize = usize;
|
||||
assert(
|
||||
sz_large_size_classes_disabled() || usize == sz_index2size(szind));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
emap_alloc_ctx_usize_get(emap_alloc_ctx_t *alloc_ctx) {
|
||||
assert(alloc_ctx->szind < SC_NSIZES);
|
||||
if (alloc_ctx->slab) {
|
||||
assert(alloc_ctx->usize == sz_index2size(alloc_ctx->szind));
|
||||
return sz_index2size(alloc_ctx->szind);
|
||||
}
|
||||
assert(sz_large_size_classes_disabled()
|
||||
|| alloc_ctx->usize == sz_index2size(alloc_ctx->szind));
|
||||
assert(alloc_ctx->usize <= SC_LARGE_MAXCLASS);
|
||||
return alloc_ctx->usize;
|
||||
}
|
||||
|
||||
/* Fills in alloc_ctx with the info in the map. */
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
emap_alloc_ctx_lookup(
|
||||
tsdn_t *tsdn, emap_t *emap, const void *ptr, emap_alloc_ctx_t *alloc_ctx) {
|
||||
EMAP_DECLARE_RTREE_CTX;
|
||||
|
||||
rtree_contents_t contents = rtree_read(
|
||||
tsdn, &emap->rtree, rtree_ctx, (uintptr_t)ptr);
|
||||
/*
|
||||
* If the alloc is invalid, do not calculate usize since edata
|
||||
* could be corrupted.
|
||||
*/
|
||||
emap_alloc_ctx_init(alloc_ctx, contents.metadata.szind,
|
||||
contents.metadata.slab,
|
||||
(contents.metadata.szind == SC_NSIZES || contents.edata == NULL)
|
||||
? 0
|
||||
: edata_usize_get(contents.edata));
|
||||
}
|
||||
|
||||
/* The pointer must be mapped. */
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
emap_full_alloc_ctx_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
|
||||
emap_full_alloc_ctx_t *full_alloc_ctx) {
|
||||
EMAP_DECLARE_RTREE_CTX;
|
||||
|
||||
rtree_contents_t contents = rtree_read(
|
||||
tsdn, &emap->rtree, rtree_ctx, (uintptr_t)ptr);
|
||||
full_alloc_ctx->edata = contents.edata;
|
||||
full_alloc_ctx->szind = contents.metadata.szind;
|
||||
full_alloc_ctx->slab = contents.metadata.slab;
|
||||
}
|
||||
|
||||
/*
|
||||
* The pointer is allowed to not be mapped.
|
||||
*
|
||||
* Returns true when the pointer is not present.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
emap_full_alloc_ctx_try_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
|
||||
emap_full_alloc_ctx_t *full_alloc_ctx) {
|
||||
EMAP_DECLARE_RTREE_CTX;
|
||||
|
||||
rtree_contents_t contents;
|
||||
bool err = rtree_read_independent(
|
||||
tsdn, &emap->rtree, rtree_ctx, (uintptr_t)ptr, &contents);
|
||||
if (err) {
|
||||
return true;
|
||||
}
|
||||
full_alloc_ctx->edata = contents.edata;
|
||||
full_alloc_ctx->szind = contents.metadata.szind;
|
||||
full_alloc_ctx->slab = contents.metadata.slab;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only used on the fastpath of free. Returns true when cannot be fulfilled by
|
||||
* fast path, e.g. when the metadata key is not cached.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
emap_alloc_ctx_try_lookup_fast(
|
||||
tsd_t *tsd, emap_t *emap, const void *ptr, emap_alloc_ctx_t *alloc_ctx) {
|
||||
/* Use the unsafe getter since this may gets called during exit. */
|
||||
rtree_ctx_t *rtree_ctx = tsd_rtree_ctxp_get_unsafe(tsd);
|
||||
|
||||
rtree_metadata_t metadata;
|
||||
bool err = rtree_metadata_try_read_fast(
|
||||
tsd_tsdn(tsd), &emap->rtree, rtree_ctx, (uintptr_t)ptr, &metadata);
|
||||
if (err) {
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* Small allocs using the fastpath can always use index to get the
|
||||
* usize. Therefore, do not set alloc_ctx->usize here.
|
||||
*/
|
||||
alloc_ctx->szind = metadata.szind;
|
||||
alloc_ctx->slab = metadata.slab;
|
||||
if (config_debug) {
|
||||
alloc_ctx->usize = SC_LARGE_MAXCLASS + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to do batch lookups out of the cache bins, which use
|
||||
* cache_bin_ptr_array_get to access the i'th element of the bin (since they
|
||||
* invert usual ordering in deciding what to flush). This lets the emap avoid
|
||||
* caring about its caller's ordering.
|
||||
*/
|
||||
typedef const void *(*emap_ptr_getter)(void *ctx, size_t ind);
|
||||
/*
|
||||
* This allows size-checking assertions, which we can only do while we're in the
|
||||
* process of edata lookups.
|
||||
*/
|
||||
typedef void (*emap_metadata_visitor)(
|
||||
void *ctx, emap_full_alloc_ctx_t *alloc_ctx);
|
||||
|
||||
typedef union emap_batch_lookup_result_u emap_batch_lookup_result_t;
|
||||
union emap_batch_lookup_result_u {
|
||||
edata_t *edata;
|
||||
rtree_leaf_elm_t *rtree_leaf;
|
||||
};
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
emap_edata_lookup_batch(tsd_t *tsd, emap_t *emap, size_t nptrs,
|
||||
emap_ptr_getter ptr_getter, void *ptr_getter_ctx,
|
||||
emap_metadata_visitor metadata_visitor, void *metadata_visitor_ctx,
|
||||
emap_batch_lookup_result_t *result) {
|
||||
/* Avoids null-checking tsdn in the loop below. */
|
||||
util_assume(tsd != NULL);
|
||||
rtree_ctx_t *rtree_ctx = tsd_rtree_ctxp_get(tsd);
|
||||
|
||||
for (size_t i = 0; i < nptrs; i++) {
|
||||
const void *ptr = ptr_getter(ptr_getter_ctx, i);
|
||||
/*
|
||||
* Reuse the edatas array as a temp buffer, lying a little about
|
||||
* the types.
|
||||
*/
|
||||
result[i].rtree_leaf = rtree_leaf_elm_lookup(tsd_tsdn(tsd),
|
||||
&emap->rtree, rtree_ctx, (uintptr_t)ptr,
|
||||
/* dependent */ true, /* init_missing */ false);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nptrs; i++) {
|
||||
rtree_leaf_elm_t *elm = result[i].rtree_leaf;
|
||||
rtree_contents_t contents = rtree_leaf_elm_read(
|
||||
tsd_tsdn(tsd), &emap->rtree, elm, /* dependent */ true);
|
||||
result[i].edata = contents.edata;
|
||||
emap_full_alloc_ctx_t alloc_ctx;
|
||||
/*
|
||||
* Not all these fields are read in practice by the metadata
|
||||
* visitor. But the compiler can easily optimize away the ones
|
||||
* that aren't, so no sense in being incomplete.
|
||||
*/
|
||||
alloc_ctx.szind = contents.metadata.szind;
|
||||
alloc_ctx.slab = contents.metadata.slab;
|
||||
alloc_ctx.edata = contents.edata;
|
||||
metadata_visitor(metadata_visitor_ctx, &alloc_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EMAP_H */
|
||||
|
|
@ -1,11 +1,16 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EMITTER_H
|
||||
#define JEMALLOC_INTERNAL_EMITTER_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/malloc_io.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
|
||||
typedef enum emitter_output_e emitter_output_t;
|
||||
enum emitter_output_e {
|
||||
emitter_output_json,
|
||||
emitter_output_json_compact,
|
||||
emitter_output_table
|
||||
};
|
||||
|
||||
|
|
@ -21,6 +26,7 @@ typedef enum emitter_type_e emitter_type_t;
|
|||
enum emitter_type_e {
|
||||
emitter_type_bool,
|
||||
emitter_type_int,
|
||||
emitter_type_int64,
|
||||
emitter_type_unsigned,
|
||||
emitter_type_uint32,
|
||||
emitter_type_uint64,
|
||||
|
|
@ -38,16 +44,18 @@ typedef struct emitter_col_s emitter_col_t;
|
|||
struct emitter_col_s {
|
||||
/* Filled in by the user. */
|
||||
emitter_justify_t justify;
|
||||
int width;
|
||||
emitter_type_t type;
|
||||
int width;
|
||||
emitter_type_t type;
|
||||
union {
|
||||
bool bool_val;
|
||||
int int_val;
|
||||
unsigned unsigned_val;
|
||||
uint32_t uint32_val;
|
||||
uint64_t uint64_val;
|
||||
size_t size_val;
|
||||
ssize_t ssize_val;
|
||||
bool bool_val;
|
||||
int int_val;
|
||||
unsigned unsigned_val;
|
||||
uint32_t uint32_val;
|
||||
uint32_t uint32_t_val;
|
||||
uint64_t uint64_val;
|
||||
uint64_t uint64_t_val;
|
||||
size_t size_val;
|
||||
ssize_t ssize_val;
|
||||
const char *str_val;
|
||||
};
|
||||
|
||||
|
|
@ -60,36 +68,23 @@ struct emitter_row_s {
|
|||
ql_head(emitter_col_t) cols;
|
||||
};
|
||||
|
||||
static inline void
|
||||
emitter_row_init(emitter_row_t *row) {
|
||||
ql_new(&row->cols);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
|
||||
ql_elm_new(col, link);
|
||||
ql_tail_insert(&row->cols, col, link);
|
||||
}
|
||||
|
||||
typedef struct emitter_s emitter_t;
|
||||
struct emitter_s {
|
||||
emitter_output_t output;
|
||||
/* The output information. */
|
||||
void (*write_cb)(void *, const char *);
|
||||
void *cbopaque;
|
||||
int nesting_depth;
|
||||
write_cb_t *write_cb;
|
||||
void *cbopaque;
|
||||
int nesting_depth;
|
||||
/* True if we've already emitted a value at the given depth. */
|
||||
bool item_at_depth;
|
||||
/* True if we emitted a key and will emit corresponding value next. */
|
||||
bool emitted_key;
|
||||
};
|
||||
|
||||
static inline void
|
||||
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
|
||||
void (*write_cb)(void *, const char *), void *cbopaque) {
|
||||
emitter->output = emitter_output;
|
||||
emitter->write_cb = write_cb;
|
||||
emitter->cbopaque = cbopaque;
|
||||
emitter->item_at_depth = false;
|
||||
emitter->nesting_depth = 0;
|
||||
static inline bool
|
||||
emitter_outputs_json(emitter_t *emitter) {
|
||||
return emitter->output == emitter_output_json
|
||||
|| emitter->output == emitter_output_json_compact;
|
||||
}
|
||||
|
||||
/* Internal convenience function. Write to the emitter the given string. */
|
||||
|
|
@ -103,34 +98,55 @@ emitter_printf(emitter_t *emitter, const char *format, ...) {
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Write to the emitter the given string, but only in table mode. */
|
||||
JEMALLOC_FORMAT_PRINTF(2, 3)
|
||||
static inline void
|
||||
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
|
||||
va_end(ap);
|
||||
static inline const char *
|
||||
JEMALLOC_FORMAT_ARG(3) emitter_gen_fmt(char *out_fmt, size_t out_size,
|
||||
const char *fmt_specifier, emitter_justify_t justify, int width) {
|
||||
size_t written;
|
||||
fmt_specifier++;
|
||||
if (justify == emitter_justify_none) {
|
||||
written = malloc_snprintf(
|
||||
out_fmt, out_size, "%%%s", fmt_specifier);
|
||||
} else if (justify == emitter_justify_left) {
|
||||
written = malloc_snprintf(
|
||||
out_fmt, out_size, "%%-%d%s", width, fmt_specifier);
|
||||
} else {
|
||||
written = malloc_snprintf(
|
||||
out_fmt, out_size, "%%%d%s", width, fmt_specifier);
|
||||
}
|
||||
/* Only happens in case of bad format string, which *we* choose. */
|
||||
assert(written < out_size);
|
||||
return out_fmt;
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
|
||||
emitter_justify_t justify, int width) {
|
||||
size_t written;
|
||||
if (justify == emitter_justify_none) {
|
||||
written = malloc_snprintf(out_fmt, out_size,
|
||||
"%%%s", fmt_specifier);
|
||||
} else if (justify == emitter_justify_left) {
|
||||
written = malloc_snprintf(out_fmt, out_size,
|
||||
"%%-%d%s", width, fmt_specifier);
|
||||
} else {
|
||||
written = malloc_snprintf(out_fmt, out_size,
|
||||
"%%%d%s", width, fmt_specifier);
|
||||
emitter_emit_str(emitter_t *emitter, emitter_justify_t justify, int width,
|
||||
char *fmt, size_t fmt_size, const char *str) {
|
||||
#define BUF_SIZE 256
|
||||
char buf[BUF_SIZE];
|
||||
size_t str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"", str);
|
||||
emitter_printf(
|
||||
emitter, emitter_gen_fmt(fmt, fmt_size, "%s", justify, width), buf);
|
||||
if (str_written < BUF_SIZE) {
|
||||
return;
|
||||
}
|
||||
/* Only happens in case of bad format string, which *we* choose. */
|
||||
assert(written < out_size);
|
||||
/*
|
||||
* There is no support for long string justification at the moment as
|
||||
* we output them partially with multiple malloc_snprintf calls and
|
||||
* justufication will work correctly only withing one call.
|
||||
* Fortunately this is not a big concern as we don't use justufication
|
||||
* with long strings right now.
|
||||
*
|
||||
* We emitted leading quotation mark and trailing '\0', hence need to
|
||||
* exclude extra characters from str shift.
|
||||
*/
|
||||
str += BUF_SIZE - 2;
|
||||
do {
|
||||
str_written = malloc_snprintf(buf, BUF_SIZE, "%s\"", str);
|
||||
str += str_written >= BUF_SIZE ? BUF_SIZE - 1 : str_written;
|
||||
emitter_printf(emitter,
|
||||
emitter_gen_fmt(fmt, fmt_size, "%s", justify, width), buf);
|
||||
} while (str_written >= BUF_SIZE);
|
||||
#undef BUF_SIZE
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -143,8 +159,6 @@ emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
|
|||
static inline void
|
||||
emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
size_t str_written;
|
||||
#define BUF_SIZE 256
|
||||
#define FMT_SIZE 10
|
||||
/*
|
||||
* We dynamically generate a format string to emit, to let us use the
|
||||
|
|
@ -153,58 +167,52 @@ emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
|
|||
* cases.
|
||||
*/
|
||||
char fmt[FMT_SIZE];
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
#define EMIT_SIMPLE(type, format) \
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width); \
|
||||
emitter_printf(emitter, fmt, *(const type *)value); \
|
||||
#define EMIT_SIMPLE(type, format) \
|
||||
emitter_printf(emitter, \
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width), \
|
||||
*(const type *)value);
|
||||
|
||||
switch (value_type) {
|
||||
case emitter_type_bool:
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
|
||||
emitter_printf(emitter, fmt, *(const bool *)value ?
|
||||
"true" : "false");
|
||||
emitter_printf(emitter,
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
|
||||
*(const bool *)value ? "true" : "false");
|
||||
break;
|
||||
case emitter_type_int:
|
||||
EMIT_SIMPLE(int, "d")
|
||||
EMIT_SIMPLE(int, "%d")
|
||||
break;
|
||||
case emitter_type_int64:
|
||||
EMIT_SIMPLE(int64_t, "%" FMTd64)
|
||||
break;
|
||||
case emitter_type_unsigned:
|
||||
EMIT_SIMPLE(unsigned, "u")
|
||||
EMIT_SIMPLE(unsigned, "%u")
|
||||
break;
|
||||
case emitter_type_ssize:
|
||||
EMIT_SIMPLE(ssize_t, "zd")
|
||||
EMIT_SIMPLE(ssize_t, "%zd")
|
||||
break;
|
||||
case emitter_type_size:
|
||||
EMIT_SIMPLE(size_t, "zu")
|
||||
EMIT_SIMPLE(size_t, "%zu")
|
||||
break;
|
||||
case emitter_type_string:
|
||||
str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
|
||||
emitter_emit_str(emitter, justify, width, fmt, FMT_SIZE,
|
||||
*(const char *const *)value);
|
||||
/*
|
||||
* We control the strings we output; we shouldn't get anything
|
||||
* anywhere near the fmt size.
|
||||
*/
|
||||
assert(str_written < BUF_SIZE);
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
|
||||
emitter_printf(emitter, fmt, buf);
|
||||
break;
|
||||
case emitter_type_uint32:
|
||||
EMIT_SIMPLE(uint32_t, FMTu32)
|
||||
EMIT_SIMPLE(uint32_t, "%" FMTu32)
|
||||
break;
|
||||
case emitter_type_uint64:
|
||||
EMIT_SIMPLE(uint64_t, FMTu64)
|
||||
EMIT_SIMPLE(uint64_t, "%" FMTu64)
|
||||
break;
|
||||
case emitter_type_title:
|
||||
EMIT_SIMPLE(char *const, "s");
|
||||
EMIT_SIMPLE(char *const, "%s");
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
#undef BUF_SIZE
|
||||
#undef FMT_SIZE
|
||||
}
|
||||
|
||||
|
||||
/* Internal functions. In json mode, tracks nesting state. */
|
||||
static inline void
|
||||
emitter_nest_inc(emitter_t *emitter) {
|
||||
|
|
@ -220,8 +228,9 @@ emitter_nest_dec(emitter_t *emitter) {
|
|||
|
||||
static inline void
|
||||
emitter_indent(emitter_t *emitter) {
|
||||
int amount = emitter->nesting_depth;
|
||||
int amount = emitter->nesting_depth;
|
||||
const char *indent_str;
|
||||
assert(emitter->output != emitter_output_json_compact);
|
||||
if (emitter->output == emitter_output_json) {
|
||||
indent_str = "\t";
|
||||
} else {
|
||||
|
|
@ -235,51 +244,156 @@ emitter_indent(emitter_t *emitter) {
|
|||
|
||||
static inline void
|
||||
emitter_json_key_prefix(emitter_t *emitter) {
|
||||
emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
|
||||
emitter_indent(emitter);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_begin(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth == 0);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
} else {
|
||||
// tabular init
|
||||
emitter_printf(emitter, "%s", "");
|
||||
assert(emitter_outputs_json(emitter));
|
||||
if (emitter->emitted_key) {
|
||||
emitter->emitted_key = false;
|
||||
return;
|
||||
}
|
||||
if (emitter->item_at_depth) {
|
||||
emitter_printf(emitter, ",");
|
||||
}
|
||||
if (emitter->output != emitter_output_json_compact) {
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Public functions for emitter_t. */
|
||||
|
||||
static inline void
|
||||
emitter_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth == 1);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n}\n");
|
||||
}
|
||||
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
|
||||
write_cb_t *write_cb, void *cbopaque) {
|
||||
emitter->output = emitter_output;
|
||||
emitter->write_cb = write_cb;
|
||||
emitter->cbopaque = cbopaque;
|
||||
emitter->item_at_depth = false;
|
||||
emitter->emitted_key = false;
|
||||
emitter->nesting_depth = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* JSON public API. */
|
||||
|
||||
/*
|
||||
* Note emits a different kv pair as well, but only in table mode. Omits the
|
||||
* note if table_note_key is NULL.
|
||||
* Emits a key (e.g. as appears in an object). The next json entity emitted will
|
||||
* be the corresponding value.
|
||||
*/
|
||||
static inline void
|
||||
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_type_t value_type, const void *value,
|
||||
const char *table_note_key, emitter_type_t table_note_value_type,
|
||||
const void *table_note_value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_json_key(emitter_t *emitter, const char *json_key) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "\"%s\": ", json_key);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
value_type, value);
|
||||
} else {
|
||||
emitter_printf(emitter, "\"%s\":%s", json_key,
|
||||
emitter->output == emitter_output_json_compact ? "" : " ");
|
||||
emitter->emitted_key = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_value(
|
||||
emitter_t *emitter, emitter_type_t value_type, const void *value) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_print_value(
|
||||
emitter, emitter_justify_none, -1, value_type, value);
|
||||
emitter->item_at_depth = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand for calling emitter_json_key and then emitter_json_value. */
|
||||
static inline void
|
||||
emitter_json_kv(emitter_t *emitter, const char *json_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_value(emitter, value_type, value);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_array_begin(emitter_t *emitter) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "[");
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand for calling emitter_json_key and then emitter_json_array_begin. */
|
||||
static inline void
|
||||
emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_array_begin(emitter);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_array_end(emitter_t *emitter) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
if (emitter->output != emitter_output_json_compact) {
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
}
|
||||
emitter_printf(emitter, "]");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_object_begin(emitter_t *emitter) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand for calling emitter_json_key and then emitter_json_object_begin. */
|
||||
static inline void
|
||||
emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_object_begin(emitter);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_object_end(emitter_t *emitter) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
if (emitter->output != emitter_output_json_compact) {
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
}
|
||||
emitter_printf(emitter, "}");
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Table public API. */
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "%s\n", table_key);
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_nest_dec(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_kv_note(emitter_t *emitter, const char *table_key,
|
||||
emitter_type_t value_type, const void *value, const char *table_note_key,
|
||||
emitter_type_t table_note_value_type, const void *table_note_value) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "%s: ", table_key);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
value_type, value);
|
||||
emitter_print_value(
|
||||
emitter, emitter_justify_none, -1, value_type, value);
|
||||
if (table_note_key != NULL) {
|
||||
emitter_printf(emitter, " (%s: ", table_note_key);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
|
|
@ -292,130 +406,22 @@ emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
|
|||
}
|
||||
|
||||
static inline void
|
||||
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_table_kv(emitter_t *emitter, const char *table_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
|
||||
emitter_table_kv_note(emitter, table_key, value_type, value, NULL,
|
||||
emitter_type_bool, NULL);
|
||||
}
|
||||
|
||||
/* Write to the emitter the given string, but only in table mode. */
|
||||
JEMALLOC_FORMAT_PRINTF(2, 3)
|
||||
static inline void
|
||||
emitter_json_kv(emitter_t *emitter, const char *json_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_kv(emitter, json_key, NULL, value_type, value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_kv(emitter_t *emitter, const char *table_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_kv(emitter, NULL, table_key, value_type, value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_begin(emitter_t *emitter, const char *json_key,
|
||||
const char *table_header) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "\"%s\": {", json_key);
|
||||
emitter_nest_inc(emitter);
|
||||
} else {
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "%s\n", table_header);
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "}");
|
||||
} else {
|
||||
emitter_nest_dec(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_dict_begin(emitter_t *emitter, const char *json_key) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_dict_begin(emitter, json_key, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_dict_end(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_dict_begin(emitter, NULL, table_key);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_dict_end(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_begin(emitter_t *emitter, const char *json_key) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "\"%s\": [", json_key);
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "]");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_obj_begin(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_obj_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "}");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,
|
||||
const void *value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
value_type, value);
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
malloc_vcprintf(
|
||||
emitter->write_cb, emitter->cbopaque, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -425,11 +431,100 @@ emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
|
|||
return;
|
||||
}
|
||||
emitter_col_t *col;
|
||||
ql_foreach(col, &row->cols, link) {
|
||||
ql_foreach (col, &row->cols, link) {
|
||||
emitter_print_value(emitter, col->justify, col->width,
|
||||
col->type, (const void *)&col->bool_val);
|
||||
}
|
||||
emitter_table_printf(emitter, "\n");
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_row_init(emitter_row_t *row) {
|
||||
ql_new(&row->cols);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
|
||||
ql_elm_new(col, link);
|
||||
ql_tail_insert(&row->cols, col, link);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Generalized public API. Emits using either JSON or table, according to
|
||||
* settings in the emitter_t. */
|
||||
|
||||
/*
|
||||
* Note emits a different kv pair as well, but only in table mode. Omits the
|
||||
* note if table_note_key is NULL.
|
||||
*/
|
||||
static inline void
|
||||
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_type_t value_type, const void *value, const char *table_note_key,
|
||||
emitter_type_t table_note_value_type, const void *table_note_value) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_value(emitter, value_type, value);
|
||||
} else {
|
||||
emitter_table_kv_note(emitter, table_key, value_type, value,
|
||||
table_note_key, table_note_value_type, table_note_value);
|
||||
}
|
||||
emitter->item_at_depth = true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
|
||||
emitter_type_bool, NULL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_begin(
|
||||
emitter_t *emitter, const char *json_key, const char *table_header) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_object_begin(emitter);
|
||||
} else {
|
||||
emitter_table_dict_begin(emitter, table_header);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_end(emitter_t *emitter) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
emitter_json_object_end(emitter);
|
||||
} else {
|
||||
emitter_table_dict_end(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_begin(emitter_t *emitter) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
assert(emitter->nesting_depth == 0);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
} else {
|
||||
/*
|
||||
* This guarantees that we always call write_cb at least once.
|
||||
* This is useful if some invariant is established by each call
|
||||
* to write_cb, but doesn't hold initially: e.g., some buffer
|
||||
* holds a null-terminated string.
|
||||
*/
|
||||
emitter_printf(emitter, "%s", "");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_end(emitter_t *emitter) {
|
||||
if (emitter_outputs_json(emitter)) {
|
||||
assert(emitter->nesting_depth == 1);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "%s",
|
||||
emitter->output == emitter_output_json_compact ? "}"
|
||||
: "\n}\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EMITTER_H */
|
||||
|
|
|
|||
78
include/jemalloc/internal/eset.h
Normal file
78
include/jemalloc/internal/eset.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#ifndef JEMALLOC_INTERNAL_ESET_H
|
||||
#define JEMALLOC_INTERNAL_ESET_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/edata.h"
|
||||
#include "jemalloc/internal/fb.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
|
||||
/*
|
||||
* An eset ("extent set") is a quantized collection of extents, with built-in
|
||||
* LRU queue.
|
||||
*
|
||||
* This class is not thread-safe; synchronization must be done externally if
|
||||
* there are mutating operations. One exception is the stats counters, which
|
||||
* may be read without any locking.
|
||||
*/
|
||||
|
||||
typedef struct eset_bin_s eset_bin_t;
|
||||
struct eset_bin_s {
|
||||
edata_heap_t heap;
|
||||
/*
|
||||
* We do first-fit across multiple size classes. If we compared against
|
||||
* the min element in each heap directly, we'd take a cache miss per
|
||||
* extent we looked at. If we co-locate the edata summaries, we only
|
||||
* take a miss on the edata we're actually going to return (which is
|
||||
* inevitable anyways).
|
||||
*/
|
||||
edata_cmp_summary_t heap_min;
|
||||
};
|
||||
|
||||
typedef struct eset_bin_stats_s eset_bin_stats_t;
|
||||
struct eset_bin_stats_s {
|
||||
atomic_zu_t nextents;
|
||||
atomic_zu_t nbytes;
|
||||
};
|
||||
|
||||
typedef struct eset_s eset_t;
|
||||
struct eset_s {
|
||||
/* Bitmap for which set bits correspond to non-empty heaps. */
|
||||
fb_group_t bitmap[FB_NGROUPS(SC_NPSIZES + 1)];
|
||||
|
||||
/* Quantized per size class heaps of extents. */
|
||||
eset_bin_t bins[SC_NPSIZES + 1];
|
||||
|
||||
eset_bin_stats_t bin_stats[SC_NPSIZES + 1];
|
||||
|
||||
/* LRU of all extents in heaps. */
|
||||
edata_list_inactive_t lru;
|
||||
|
||||
/* Page sum for all extents in heaps. */
|
||||
atomic_zu_t npages;
|
||||
|
||||
/*
|
||||
* A duplication of the data in the containing ecache. We use this only
|
||||
* for assertions on the states of the passed-in extents.
|
||||
*/
|
||||
extent_state_t state;
|
||||
};
|
||||
|
||||
void eset_init(eset_t *eset, extent_state_t state);
|
||||
|
||||
size_t eset_npages_get(eset_t *eset);
|
||||
/* Get the number of extents in the given page size index. */
|
||||
size_t eset_nextents_get(eset_t *eset, pszind_t ind);
|
||||
/* Get the sum total bytes of the extents in the given page size index. */
|
||||
size_t eset_nbytes_get(eset_t *eset, pszind_t ind);
|
||||
|
||||
void eset_insert(eset_t *eset, edata_t *edata);
|
||||
void eset_remove(eset_t *eset, edata_t *edata);
|
||||
/*
|
||||
* Select an extent from this eset of the given size and alignment. Returns
|
||||
* null if no such item could be found.
|
||||
*/
|
||||
edata_t *eset_fit(eset_t *eset, size_t esize, size_t alignment, bool exact_only,
|
||||
unsigned lg_max_fit);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ESET_H */
|
||||
50
include/jemalloc/internal/exp_grow.h
Normal file
50
include/jemalloc/internal/exp_grow.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXP_GROW_H
|
||||
#define JEMALLOC_INTERNAL_EXP_GROW_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
typedef struct exp_grow_s exp_grow_t;
|
||||
struct exp_grow_s {
|
||||
/*
|
||||
* Next extent size class in a growing series to use when satisfying a
|
||||
* request via the extent hooks (only if opt_retain). This limits the
|
||||
* number of disjoint virtual memory ranges so that extent merging can
|
||||
* be effective even if multiple arenas' extent allocation requests are
|
||||
* highly interleaved.
|
||||
*
|
||||
* retain_grow_limit is the max allowed size ind to expand (unless the
|
||||
* required size is greater). Default is no limit, and controlled
|
||||
* through mallctl only.
|
||||
*/
|
||||
pszind_t next;
|
||||
pszind_t limit;
|
||||
};
|
||||
|
||||
static inline bool
|
||||
exp_grow_size_prepare(exp_grow_t *exp_grow, size_t alloc_size_min,
|
||||
size_t *r_alloc_size, pszind_t *r_skip) {
|
||||
*r_skip = 0;
|
||||
*r_alloc_size = sz_pind2sz(exp_grow->next + *r_skip);
|
||||
while (*r_alloc_size < alloc_size_min) {
|
||||
(*r_skip)++;
|
||||
if (exp_grow->next + *r_skip >= sz_psz2ind(SC_LARGE_MAXCLASS)) {
|
||||
/* Outside legal range. */
|
||||
return true;
|
||||
}
|
||||
*r_alloc_size = sz_pind2sz(exp_grow->next + *r_skip);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
exp_grow_size_commit(exp_grow_t *exp_grow, pszind_t skip) {
|
||||
if (exp_grow->next + skip + 1 <= exp_grow->limit) {
|
||||
exp_grow->next += skip + 1;
|
||||
} else {
|
||||
exp_grow->next = exp_grow->limit;
|
||||
}
|
||||
}
|
||||
|
||||
void exp_grow_init(exp_grow_t *exp_grow);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXP_GROW_H */
|
||||
148
include/jemalloc/internal/extent.h
Normal file
148
include/jemalloc/internal/extent.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTENT_H
|
||||
#define JEMALLOC_INTERNAL_EXTENT_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/ecache.h"
|
||||
#include "jemalloc/internal/ehooks.h"
|
||||
#include "jemalloc/internal/pac.h"
|
||||
#include "jemalloc/internal/ph.h"
|
||||
#include "jemalloc/internal/rtree.h"
|
||||
|
||||
/*
|
||||
* This module contains the page-level allocator. It chooses the addresses that
|
||||
* allocations requested by other modules will inhabit, and updates the global
|
||||
* metadata to reflect allocation/deallocation/purging decisions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)
|
||||
* is the max ratio between the size of the active extent and the new extent.
|
||||
*/
|
||||
#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6
|
||||
extern size_t opt_lg_extent_max_active_fit;
|
||||
|
||||
#define PROCESS_MADVISE_MAX_BATCH_DEFAULT 0
|
||||
extern size_t opt_process_madvise_max_batch;
|
||||
|
||||
#ifdef JEMALLOC_HAVE_PROCESS_MADVISE
|
||||
/* The iovec is on stack. Limit the max batch to avoid stack overflow. */
|
||||
# define PROCESS_MADVISE_MAX_BATCH_LIMIT \
|
||||
(VARIABLE_ARRAY_SIZE_MAX / sizeof(struct iovec))
|
||||
#else
|
||||
# define PROCESS_MADVISE_MAX_BATCH_LIMIT 0
|
||||
#endif
|
||||
|
||||
edata_t *ecache_alloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
|
||||
ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
|
||||
bool zero, bool guarded);
|
||||
edata_t *ecache_alloc_grow(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
|
||||
ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
|
||||
bool zero, bool guarded);
|
||||
void ecache_dalloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
|
||||
edata_t *edata);
|
||||
edata_t *ecache_evict(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
|
||||
ecache_t *ecache, size_t npages_min);
|
||||
|
||||
void extent_gdump_add(tsdn_t *tsdn, const edata_t *edata);
|
||||
void extent_record(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
|
||||
edata_t *edata);
|
||||
void extent_dalloc_gap(
|
||||
tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *edata);
|
||||
edata_t *extent_alloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
|
||||
void *new_addr, size_t size, size_t alignment, bool zero, bool *commit,
|
||||
bool growing_retained);
|
||||
void extent_dalloc_wrapper(
|
||||
tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *edata);
|
||||
void extent_dalloc_wrapper_purged(
|
||||
tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *edata);
|
||||
void extent_destroy_wrapper(
|
||||
tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *edata);
|
||||
bool extent_purge_lazy_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
||||
size_t offset, size_t length);
|
||||
bool extent_purge_forced_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
||||
size_t offset, size_t length);
|
||||
edata_t *extent_split_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
|
||||
edata_t *edata, size_t size_a, size_t size_b, bool holding_core_locks);
|
||||
bool extent_merge_wrapper(
|
||||
tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *a, edata_t *b);
|
||||
bool extent_commit_zero(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
||||
bool commit, bool zero, bool growing_retained);
|
||||
size_t extent_sn_next(pac_t *pac);
|
||||
bool extent_boot(void);
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
extent_neighbor_head_state_mergeable(
|
||||
bool edata_is_head, bool neighbor_is_head, bool forward) {
|
||||
/*
|
||||
* Head states checking: disallow merging if the higher addr extent is a
|
||||
* head extent. This helps preserve first-fit, and more importantly
|
||||
* makes sure no merge across arenas.
|
||||
*/
|
||||
if (forward) {
|
||||
if (neighbor_is_head) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (edata_is_head) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
extent_can_acquire_neighbor(edata_t *edata, rtree_contents_t contents,
|
||||
extent_pai_t pai, extent_state_t expected_state, bool forward,
|
||||
bool expanding) {
|
||||
edata_t *neighbor = contents.edata;
|
||||
if (neighbor == NULL) {
|
||||
return false;
|
||||
}
|
||||
/* It's not safe to access *neighbor yet; must verify states first. */
|
||||
bool neighbor_is_head = contents.metadata.is_head;
|
||||
if (!extent_neighbor_head_state_mergeable(
|
||||
edata_is_head_get(edata), neighbor_is_head, forward)) {
|
||||
return false;
|
||||
}
|
||||
extent_state_t neighbor_state = contents.metadata.state;
|
||||
if (pai == EXTENT_PAI_PAC) {
|
||||
if (neighbor_state != expected_state) {
|
||||
return false;
|
||||
}
|
||||
/* From this point, it's safe to access *neighbor. */
|
||||
if (!expanding
|
||||
&& (edata_committed_get(edata)
|
||||
!= edata_committed_get(neighbor))) {
|
||||
/*
|
||||
* Some platforms (e.g. Windows) require an explicit
|
||||
* commit step (and writing to uncommitted memory is not
|
||||
* allowed).
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (neighbor_state == extent_state_active) {
|
||||
return false;
|
||||
}
|
||||
/* From this point, it's safe to access *neighbor. */
|
||||
}
|
||||
|
||||
assert(edata_pai_get(edata) == pai);
|
||||
if (edata_pai_get(neighbor) != pai) {
|
||||
return false;
|
||||
}
|
||||
if (opt_retain) {
|
||||
assert(edata_arena_ind_get(edata)
|
||||
== edata_arena_ind_get(neighbor));
|
||||
} else {
|
||||
if (edata_arena_ind_get(edata)
|
||||
!= edata_arena_ind_get(neighbor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert(!edata_guarded_get(edata) && !edata_guarded_get(neighbor));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_H */
|
||||
|
|
@ -1,26 +1,30 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTENT_DSS_H
|
||||
#define JEMALLOC_INTERNAL_EXTENT_DSS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_types.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
typedef enum {
|
||||
dss_prec_disabled = 0,
|
||||
dss_prec_primary = 1,
|
||||
dss_prec_disabled = 0,
|
||||
dss_prec_primary = 1,
|
||||
dss_prec_secondary = 2,
|
||||
|
||||
dss_prec_limit = 3
|
||||
dss_prec_limit = 3
|
||||
} dss_prec_t;
|
||||
#define DSS_PREC_DEFAULT dss_prec_secondary
|
||||
#define DSS_DEFAULT "secondary"
|
||||
|
||||
extern const char *dss_prec_names[];
|
||||
extern const char *const dss_prec_names[];
|
||||
|
||||
extern const char *opt_dss;
|
||||
|
||||
dss_prec_t extent_dss_prec_get(void);
|
||||
bool extent_dss_prec_set(dss_prec_t dss_prec);
|
||||
void *extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool *commit);
|
||||
bool extent_in_dss(void *addr);
|
||||
bool extent_dss_mergeable(void *addr_a, void *addr_b);
|
||||
void extent_dss_boot(void);
|
||||
bool extent_dss_prec_set(dss_prec_t dss_prec);
|
||||
void *extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool *commit);
|
||||
bool extent_in_dss(void *addr);
|
||||
bool extent_dss_mergeable(void *addr_a, void *addr_b);
|
||||
void extent_dss_boot(void);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_DSS_H */
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTENT_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_EXTENT_EXTERNS_H
|
||||
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/mutex_pool.h"
|
||||
#include "jemalloc/internal/ph.h"
|
||||
#include "jemalloc/internal/rtree.h"
|
||||
|
||||
extern size_t opt_lg_extent_max_active_fit;
|
||||
|
||||
extern rtree_t extents_rtree;
|
||||
extern const extent_hooks_t extent_hooks_default;
|
||||
extern mutex_pool_t extent_mutex_pool;
|
||||
|
||||
extent_t *extent_alloc(tsdn_t *tsdn, arena_t *arena);
|
||||
void extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent);
|
||||
|
||||
extent_hooks_t *extent_hooks_get(arena_t *arena);
|
||||
extent_hooks_t *extent_hooks_set(tsd_t *tsd, arena_t *arena,
|
||||
extent_hooks_t *extent_hooks);
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
size_t extent_size_quantize_floor(size_t size);
|
||||
size_t extent_size_quantize_ceil(size_t size);
|
||||
#endif
|
||||
|
||||
rb_proto(, extent_avail_, extent_tree_t, extent_t)
|
||||
ph_proto(, extent_heap_, extent_heap_t, extent_t)
|
||||
|
||||
bool extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,
|
||||
bool delay_coalesce);
|
||||
extent_state_t extents_state_get(const extents_t *extents);
|
||||
size_t extents_npages_get(extents_t *extents);
|
||||
extent_t *extents_alloc(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,
|
||||
size_t size, size_t pad, size_t alignment, bool slab, szind_t szind,
|
||||
bool *zero, bool *commit);
|
||||
void extents_dalloc(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent);
|
||||
extent_t *extents_evict(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_min);
|
||||
void extents_prefork(tsdn_t *tsdn, extents_t *extents);
|
||||
void extents_postfork_parent(tsdn_t *tsdn, extents_t *extents);
|
||||
void extents_postfork_child(tsdn_t *tsdn, extents_t *extents);
|
||||
extent_t *extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,
|
||||
size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit);
|
||||
void extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent);
|
||||
void extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent);
|
||||
void extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent);
|
||||
bool extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
|
||||
size_t length);
|
||||
bool extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
|
||||
size_t length);
|
||||
bool extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
|
||||
size_t length);
|
||||
bool extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
|
||||
size_t length);
|
||||
extent_t *extent_split_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,
|
||||
szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b);
|
||||
bool extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b);
|
||||
|
||||
bool extent_boot(void);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_EXTERNS_H */
|
||||
|
|
@ -1,433 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTENT_INLINES_H
|
||||
#define JEMALLOC_INTERNAL_EXTENT_INLINES_H
|
||||
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/mutex_pool.h"
|
||||
#include "jemalloc/internal/pages.h"
|
||||
#include "jemalloc/internal/prng.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
|
||||
static inline void
|
||||
extent_lock(tsdn_t *tsdn, extent_t *extent) {
|
||||
assert(extent != NULL);
|
||||
mutex_pool_lock(tsdn, &extent_mutex_pool, (uintptr_t)extent);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_unlock(tsdn_t *tsdn, extent_t *extent) {
|
||||
assert(extent != NULL);
|
||||
mutex_pool_unlock(tsdn, &extent_mutex_pool, (uintptr_t)extent);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_lock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {
|
||||
assert(extent1 != NULL && extent2 != NULL);
|
||||
mutex_pool_lock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,
|
||||
(uintptr_t)extent2);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_unlock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {
|
||||
assert(extent1 != NULL && extent2 != NULL);
|
||||
mutex_pool_unlock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,
|
||||
(uintptr_t)extent2);
|
||||
}
|
||||
|
||||
static inline arena_t *
|
||||
extent_arena_get(const extent_t *extent) {
|
||||
unsigned arena_ind = (unsigned)((extent->e_bits &
|
||||
EXTENT_BITS_ARENA_MASK) >> EXTENT_BITS_ARENA_SHIFT);
|
||||
/*
|
||||
* The following check is omitted because we should never actually read
|
||||
* a NULL arena pointer.
|
||||
*/
|
||||
if (false && arena_ind >= MALLOCX_ARENA_LIMIT) {
|
||||
return NULL;
|
||||
}
|
||||
assert(arena_ind < MALLOCX_ARENA_LIMIT);
|
||||
return (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline szind_t
|
||||
extent_szind_get_maybe_invalid(const extent_t *extent) {
|
||||
szind_t szind = (szind_t)((extent->e_bits & EXTENT_BITS_SZIND_MASK) >>
|
||||
EXTENT_BITS_SZIND_SHIFT);
|
||||
assert(szind <= NSIZES);
|
||||
return szind;
|
||||
}
|
||||
|
||||
static inline szind_t
|
||||
extent_szind_get(const extent_t *extent) {
|
||||
szind_t szind = extent_szind_get_maybe_invalid(extent);
|
||||
assert(szind < NSIZES); /* Never call when "invalid". */
|
||||
return szind;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
extent_usize_get(const extent_t *extent) {
|
||||
return sz_index2size(extent_szind_get(extent));
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
extent_sn_get(const extent_t *extent) {
|
||||
return (size_t)((extent->e_bits & EXTENT_BITS_SN_MASK) >>
|
||||
EXTENT_BITS_SN_SHIFT);
|
||||
}
|
||||
|
||||
static inline extent_state_t
|
||||
extent_state_get(const extent_t *extent) {
|
||||
return (extent_state_t)((extent->e_bits & EXTENT_BITS_STATE_MASK) >>
|
||||
EXTENT_BITS_STATE_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
extent_zeroed_get(const extent_t *extent) {
|
||||
return (bool)((extent->e_bits & EXTENT_BITS_ZEROED_MASK) >>
|
||||
EXTENT_BITS_ZEROED_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
extent_committed_get(const extent_t *extent) {
|
||||
return (bool)((extent->e_bits & EXTENT_BITS_COMMITTED_MASK) >>
|
||||
EXTENT_BITS_COMMITTED_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
extent_dumpable_get(const extent_t *extent) {
|
||||
return (bool)((extent->e_bits & EXTENT_BITS_DUMPABLE_MASK) >>
|
||||
EXTENT_BITS_DUMPABLE_SHIFT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
extent_slab_get(const extent_t *extent) {
|
||||
return (bool)((extent->e_bits & EXTENT_BITS_SLAB_MASK) >>
|
||||
EXTENT_BITS_SLAB_SHIFT);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
extent_nfree_get(const extent_t *extent) {
|
||||
assert(extent_slab_get(extent));
|
||||
return (unsigned)((extent->e_bits & EXTENT_BITS_NFREE_MASK) >>
|
||||
EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
extent_base_get(const extent_t *extent) {
|
||||
assert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||
|
||||
!extent_slab_get(extent));
|
||||
return PAGE_ADDR2BASE(extent->e_addr);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
extent_addr_get(const extent_t *extent) {
|
||||
assert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||
|
||||
!extent_slab_get(extent));
|
||||
return extent->e_addr;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
extent_size_get(const extent_t *extent) {
|
||||
return (extent->e_size_esn & EXTENT_SIZE_MASK);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
extent_esn_get(const extent_t *extent) {
|
||||
return (extent->e_size_esn & EXTENT_ESN_MASK);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
extent_bsize_get(const extent_t *extent) {
|
||||
return extent->e_bsize;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
extent_before_get(const extent_t *extent) {
|
||||
return (void *)((uintptr_t)extent_base_get(extent) - PAGE);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
extent_last_get(const extent_t *extent) {
|
||||
return (void *)((uintptr_t)extent_base_get(extent) +
|
||||
extent_size_get(extent) - PAGE);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
extent_past_get(const extent_t *extent) {
|
||||
return (void *)((uintptr_t)extent_base_get(extent) +
|
||||
extent_size_get(extent));
|
||||
}
|
||||
|
||||
static inline arena_slab_data_t *
|
||||
extent_slab_data_get(extent_t *extent) {
|
||||
assert(extent_slab_get(extent));
|
||||
return &extent->e_slab_data;
|
||||
}
|
||||
|
||||
static inline const arena_slab_data_t *
|
||||
extent_slab_data_get_const(const extent_t *extent) {
|
||||
assert(extent_slab_get(extent));
|
||||
return &extent->e_slab_data;
|
||||
}
|
||||
|
||||
static inline prof_tctx_t *
|
||||
extent_prof_tctx_get(const extent_t *extent) {
|
||||
return (prof_tctx_t *)atomic_load_p(&extent->e_prof_tctx,
|
||||
ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_arena_set(extent_t *extent, arena_t *arena) {
|
||||
unsigned arena_ind = (arena != NULL) ? arena_ind_get(arena) : ((1U <<
|
||||
MALLOCX_ARENA_BITS) - 1);
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_ARENA_MASK) |
|
||||
((uint64_t)arena_ind << EXTENT_BITS_ARENA_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_addr_set(extent_t *extent, void *addr) {
|
||||
extent->e_addr = addr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_addr_randomize(UNUSED tsdn_t *tsdn, extent_t *extent, size_t alignment) {
|
||||
assert(extent_base_get(extent) == extent_addr_get(extent));
|
||||
|
||||
if (alignment < PAGE) {
|
||||
unsigned lg_range = LG_PAGE -
|
||||
lg_floor(CACHELINE_CEILING(alignment));
|
||||
size_t r;
|
||||
if (!tsdn_null(tsdn)) {
|
||||
tsd_t *tsd = tsdn_tsd(tsdn);
|
||||
r = (size_t)prng_lg_range_u64(
|
||||
tsd_offset_statep_get(tsd), lg_range);
|
||||
} else {
|
||||
r = prng_lg_range_zu(
|
||||
&extent_arena_get(extent)->offset_state,
|
||||
lg_range, true);
|
||||
}
|
||||
uintptr_t random_offset = ((uintptr_t)r) << (LG_PAGE -
|
||||
lg_range);
|
||||
extent->e_addr = (void *)((uintptr_t)extent->e_addr +
|
||||
random_offset);
|
||||
assert(ALIGNMENT_ADDR2BASE(extent->e_addr, alignment) ==
|
||||
extent->e_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_size_set(extent_t *extent, size_t size) {
|
||||
assert((size & ~EXTENT_SIZE_MASK) == 0);
|
||||
extent->e_size_esn = size | (extent->e_size_esn & ~EXTENT_SIZE_MASK);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_esn_set(extent_t *extent, size_t esn) {
|
||||
extent->e_size_esn = (extent->e_size_esn & ~EXTENT_ESN_MASK) | (esn &
|
||||
EXTENT_ESN_MASK);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_bsize_set(extent_t *extent, size_t bsize) {
|
||||
extent->e_bsize = bsize;
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_szind_set(extent_t *extent, szind_t szind) {
|
||||
assert(szind <= NSIZES); /* NSIZES means "invalid". */
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SZIND_MASK) |
|
||||
((uint64_t)szind << EXTENT_BITS_SZIND_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_set(extent_t *extent, unsigned nfree) {
|
||||
assert(extent_slab_get(extent));
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_NFREE_MASK) |
|
||||
((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_inc(extent_t *extent) {
|
||||
assert(extent_slab_get(extent));
|
||||
extent->e_bits += ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_dec(extent_t *extent) {
|
||||
assert(extent_slab_get(extent));
|
||||
extent->e_bits -= ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_sn_set(extent_t *extent, size_t sn) {
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SN_MASK) |
|
||||
((uint64_t)sn << EXTENT_BITS_SN_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_state_set(extent_t *extent, extent_state_t state) {
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_STATE_MASK) |
|
||||
((uint64_t)state << EXTENT_BITS_STATE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_zeroed_set(extent_t *extent, bool zeroed) {
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_ZEROED_MASK) |
|
||||
((uint64_t)zeroed << EXTENT_BITS_ZEROED_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_committed_set(extent_t *extent, bool committed) {
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_COMMITTED_MASK) |
|
||||
((uint64_t)committed << EXTENT_BITS_COMMITTED_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_dumpable_set(extent_t *extent, bool dumpable) {
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_DUMPABLE_MASK) |
|
||||
((uint64_t)dumpable << EXTENT_BITS_DUMPABLE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_slab_set(extent_t *extent, bool slab) {
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SLAB_MASK) |
|
||||
((uint64_t)slab << EXTENT_BITS_SLAB_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_prof_tctx_set(extent_t *extent, prof_tctx_t *tctx) {
|
||||
atomic_store_p(&extent->e_prof_tctx, tctx, ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_init(extent_t *extent, arena_t *arena, void *addr, size_t size,
|
||||
bool slab, szind_t szind, size_t sn, extent_state_t state, bool zeroed,
|
||||
bool committed, bool dumpable) {
|
||||
assert(addr == PAGE_ADDR2BASE(addr) || !slab);
|
||||
|
||||
extent_arena_set(extent, arena);
|
||||
extent_addr_set(extent, addr);
|
||||
extent_size_set(extent, size);
|
||||
extent_slab_set(extent, slab);
|
||||
extent_szind_set(extent, szind);
|
||||
extent_sn_set(extent, sn);
|
||||
extent_state_set(extent, state);
|
||||
extent_zeroed_set(extent, zeroed);
|
||||
extent_committed_set(extent, committed);
|
||||
extent_dumpable_set(extent, dumpable);
|
||||
ql_elm_new(extent, ql_link);
|
||||
if (config_prof) {
|
||||
extent_prof_tctx_set(extent, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_binit(extent_t *extent, void *addr, size_t bsize, size_t sn) {
|
||||
extent_arena_set(extent, NULL);
|
||||
extent_addr_set(extent, addr);
|
||||
extent_bsize_set(extent, bsize);
|
||||
extent_slab_set(extent, false);
|
||||
extent_szind_set(extent, NSIZES);
|
||||
extent_sn_set(extent, sn);
|
||||
extent_state_set(extent, extent_state_active);
|
||||
extent_zeroed_set(extent, true);
|
||||
extent_committed_set(extent, true);
|
||||
extent_dumpable_set(extent, true);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_list_init(extent_list_t *list) {
|
||||
ql_new(list);
|
||||
}
|
||||
|
||||
static inline extent_t *
|
||||
extent_list_first(const extent_list_t *list) {
|
||||
return ql_first(list);
|
||||
}
|
||||
|
||||
static inline extent_t *
|
||||
extent_list_last(const extent_list_t *list) {
|
||||
return ql_last(list, ql_link);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_list_append(extent_list_t *list, extent_t *extent) {
|
||||
ql_tail_insert(list, extent, ql_link);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_list_prepend(extent_list_t *list, extent_t *extent) {
|
||||
ql_head_insert(list, extent, ql_link);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_list_replace(extent_list_t *list, extent_t *to_remove,
|
||||
extent_t *to_insert) {
|
||||
ql_after_insert(to_remove, to_insert, ql_link);
|
||||
ql_remove(list, to_remove, ql_link);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_list_remove(extent_list_t *list, extent_t *extent) {
|
||||
ql_remove(list, extent, ql_link);
|
||||
}
|
||||
|
||||
static inline int
|
||||
extent_sn_comp(const extent_t *a, const extent_t *b) {
|
||||
size_t a_sn = extent_sn_get(a);
|
||||
size_t b_sn = extent_sn_get(b);
|
||||
|
||||
return (a_sn > b_sn) - (a_sn < b_sn);
|
||||
}
|
||||
|
||||
static inline int
|
||||
extent_esn_comp(const extent_t *a, const extent_t *b) {
|
||||
size_t a_esn = extent_esn_get(a);
|
||||
size_t b_esn = extent_esn_get(b);
|
||||
|
||||
return (a_esn > b_esn) - (a_esn < b_esn);
|
||||
}
|
||||
|
||||
static inline int
|
||||
extent_ad_comp(const extent_t *a, const extent_t *b) {
|
||||
uintptr_t a_addr = (uintptr_t)extent_addr_get(a);
|
||||
uintptr_t b_addr = (uintptr_t)extent_addr_get(b);
|
||||
|
||||
return (a_addr > b_addr) - (a_addr < b_addr);
|
||||
}
|
||||
|
||||
static inline int
|
||||
extent_ead_comp(const extent_t *a, const extent_t *b) {
|
||||
uintptr_t a_eaddr = (uintptr_t)a;
|
||||
uintptr_t b_eaddr = (uintptr_t)b;
|
||||
|
||||
return (a_eaddr > b_eaddr) - (a_eaddr < b_eaddr);
|
||||
}
|
||||
|
||||
static inline int
|
||||
extent_snad_comp(const extent_t *a, const extent_t *b) {
|
||||
int ret;
|
||||
|
||||
ret = extent_sn_comp(a, b);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = extent_ad_comp(a, b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
extent_esnead_comp(const extent_t *a, const extent_t *b) {
|
||||
int ret;
|
||||
|
||||
ret = extent_esn_comp(a, b);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = extent_ead_comp(a, b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_INLINES_H */
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
|
||||
extern bool opt_retain;
|
||||
|
||||
void *extent_alloc_mmap(void *new_addr, size_t size, size_t alignment,
|
||||
bool *zero, bool *commit);
|
||||
void *extent_alloc_mmap(
|
||||
void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit);
|
||||
bool extent_dalloc_mmap(void *addr, size_t size);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H */
|
||||
|
|
|
|||
|
|
@ -1,219 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTENT_STRUCTS_H
|
||||
#define JEMALLOC_INTERNAL_EXTENT_STRUCTS_H
|
||||
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/ph.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
|
||||
typedef enum {
|
||||
extent_state_active = 0,
|
||||
extent_state_dirty = 1,
|
||||
extent_state_muzzy = 2,
|
||||
extent_state_retained = 3
|
||||
} extent_state_t;
|
||||
|
||||
/* Extent (span of pages). Use accessor functions for e_* fields. */
|
||||
struct extent_s {
|
||||
/*
|
||||
* Bitfield containing several fields:
|
||||
*
|
||||
* a: arena_ind
|
||||
* b: slab
|
||||
* c: committed
|
||||
* d: dumpable
|
||||
* z: zeroed
|
||||
* t: state
|
||||
* i: szind
|
||||
* f: nfree
|
||||
* n: sn
|
||||
*
|
||||
* nnnnnnnn ... nnnnffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa
|
||||
*
|
||||
* arena_ind: Arena from which this extent came, or all 1 bits if
|
||||
* unassociated.
|
||||
*
|
||||
* slab: The slab flag indicates whether the extent is used for a slab
|
||||
* of small regions. This helps differentiate small size classes,
|
||||
* and it indicates whether interior pointers can be looked up via
|
||||
* iealloc().
|
||||
*
|
||||
* committed: The committed flag indicates whether physical memory is
|
||||
* committed to the extent, whether explicitly or implicitly
|
||||
* as on a system that overcommits and satisfies physical
|
||||
* memory needs on demand via soft page faults.
|
||||
*
|
||||
* dumpable: The dumpable flag indicates whether or not we've set the
|
||||
* memory in question to be dumpable. Note that this
|
||||
* interacts somewhat subtly with user-specified extent hooks,
|
||||
* since we don't know if *they* are fiddling with
|
||||
* dumpability (in which case, we don't want to undo whatever
|
||||
* they're doing). To deal with this scenario, we:
|
||||
* - Make dumpable false only for memory allocated with the
|
||||
* default hooks.
|
||||
* - Only allow memory to go from non-dumpable to dumpable,
|
||||
* and only once.
|
||||
* - Never make the OS call to allow dumping when the
|
||||
* dumpable bit is already set.
|
||||
* These three constraints mean that we will never
|
||||
* accidentally dump user memory that the user meant to set
|
||||
* nondumpable with their extent hooks.
|
||||
*
|
||||
*
|
||||
* zeroed: The zeroed flag is used by extent recycling code to track
|
||||
* whether memory is zero-filled.
|
||||
*
|
||||
* state: The state flag is an extent_state_t.
|
||||
*
|
||||
* szind: The szind flag indicates usable size class index for
|
||||
* allocations residing in this extent, regardless of whether the
|
||||
* extent is a slab. Extent size and usable size often differ
|
||||
* even for non-slabs, either due to sz_large_pad or promotion of
|
||||
* sampled small regions.
|
||||
*
|
||||
* nfree: Number of free regions in slab.
|
||||
*
|
||||
* sn: Serial number (potentially non-unique).
|
||||
*
|
||||
* Serial numbers may wrap around if !opt_retain, but as long as
|
||||
* comparison functions fall back on address comparison for equal
|
||||
* serial numbers, stable (if imperfect) ordering is maintained.
|
||||
*
|
||||
* Serial numbers may not be unique even in the absence of
|
||||
* wrap-around, e.g. when splitting an extent and assigning the same
|
||||
* serial number to both resulting adjacent extents.
|
||||
*/
|
||||
uint64_t e_bits;
|
||||
#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) ((((((uint64_t)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) << (CURRENT_FIELD_SHIFT))
|
||||
|
||||
#define EXTENT_BITS_ARENA_WIDTH MALLOCX_ARENA_BITS
|
||||
#define EXTENT_BITS_ARENA_SHIFT 0
|
||||
#define EXTENT_BITS_ARENA_MASK MASK(EXTENT_BITS_ARENA_WIDTH, EXTENT_BITS_ARENA_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_SLAB_WIDTH 1
|
||||
#define EXTENT_BITS_SLAB_SHIFT (EXTENT_BITS_ARENA_WIDTH + EXTENT_BITS_ARENA_SHIFT)
|
||||
#define EXTENT_BITS_SLAB_MASK MASK(EXTENT_BITS_SLAB_WIDTH, EXTENT_BITS_SLAB_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_COMMITTED_WIDTH 1
|
||||
#define EXTENT_BITS_COMMITTED_SHIFT (EXTENT_BITS_SLAB_WIDTH + EXTENT_BITS_SLAB_SHIFT)
|
||||
#define EXTENT_BITS_COMMITTED_MASK MASK(EXTENT_BITS_COMMITTED_WIDTH, EXTENT_BITS_COMMITTED_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_DUMPABLE_WIDTH 1
|
||||
#define EXTENT_BITS_DUMPABLE_SHIFT (EXTENT_BITS_COMMITTED_WIDTH + EXTENT_BITS_COMMITTED_SHIFT)
|
||||
#define EXTENT_BITS_DUMPABLE_MASK MASK(EXTENT_BITS_DUMPABLE_WIDTH, EXTENT_BITS_DUMPABLE_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_ZEROED_WIDTH 1
|
||||
#define EXTENT_BITS_ZEROED_SHIFT (EXTENT_BITS_DUMPABLE_WIDTH + EXTENT_BITS_DUMPABLE_SHIFT)
|
||||
#define EXTENT_BITS_ZEROED_MASK MASK(EXTENT_BITS_ZEROED_WIDTH, EXTENT_BITS_ZEROED_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_STATE_WIDTH 2
|
||||
#define EXTENT_BITS_STATE_SHIFT (EXTENT_BITS_ZEROED_WIDTH + EXTENT_BITS_ZEROED_SHIFT)
|
||||
#define EXTENT_BITS_STATE_MASK MASK(EXTENT_BITS_STATE_WIDTH, EXTENT_BITS_STATE_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_SZIND_WIDTH LG_CEIL_NSIZES
|
||||
#define EXTENT_BITS_SZIND_SHIFT (EXTENT_BITS_STATE_WIDTH + EXTENT_BITS_STATE_SHIFT)
|
||||
#define EXTENT_BITS_SZIND_MASK MASK(EXTENT_BITS_SZIND_WIDTH, EXTENT_BITS_SZIND_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_NFREE_WIDTH (LG_SLAB_MAXREGS + 1)
|
||||
#define EXTENT_BITS_NFREE_SHIFT (EXTENT_BITS_SZIND_WIDTH + EXTENT_BITS_SZIND_SHIFT)
|
||||
#define EXTENT_BITS_NFREE_MASK MASK(EXTENT_BITS_NFREE_WIDTH, EXTENT_BITS_NFREE_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_SN_SHIFT (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)
|
||||
#define EXTENT_BITS_SN_MASK (UINT64_MAX << EXTENT_BITS_SN_SHIFT)
|
||||
|
||||
/* Pointer to the extent that this structure is responsible for. */
|
||||
void *e_addr;
|
||||
|
||||
union {
|
||||
/*
|
||||
* Extent size and serial number associated with the extent
|
||||
* structure (different than the serial number for the extent at
|
||||
* e_addr).
|
||||
*
|
||||
* ssssssss [...] ssssssss ssssnnnn nnnnnnnn
|
||||
*/
|
||||
size_t e_size_esn;
|
||||
#define EXTENT_SIZE_MASK ((size_t)~(PAGE-1))
|
||||
#define EXTENT_ESN_MASK ((size_t)PAGE-1)
|
||||
/* Base extent size, which may not be a multiple of PAGE. */
|
||||
size_t e_bsize;
|
||||
};
|
||||
|
||||
/*
|
||||
* List linkage, used by a variety of lists:
|
||||
* - bin_t's slabs_full
|
||||
* - extents_t's LRU
|
||||
* - stashed dirty extents
|
||||
* - arena's large allocations
|
||||
*/
|
||||
ql_elm(extent_t) ql_link;
|
||||
|
||||
/*
|
||||
* Linkage for per size class sn/address-ordered heaps, and
|
||||
* for extent_avail
|
||||
*/
|
||||
phn(extent_t) ph_link;
|
||||
|
||||
union {
|
||||
/* Small region slab metadata. */
|
||||
arena_slab_data_t e_slab_data;
|
||||
|
||||
/*
|
||||
* Profile counters, used for large objects. Points to a
|
||||
* prof_tctx_t.
|
||||
*/
|
||||
atomic_p_t e_prof_tctx;
|
||||
};
|
||||
};
|
||||
typedef ql_head(extent_t) extent_list_t;
|
||||
typedef ph(extent_t) extent_tree_t;
|
||||
typedef ph(extent_t) extent_heap_t;
|
||||
|
||||
/* Quantized collection of extents, with built-in LRU queue. */
|
||||
struct extents_s {
|
||||
malloc_mutex_t mtx;
|
||||
|
||||
/*
|
||||
* Quantized per size class heaps of extents.
|
||||
*
|
||||
* Synchronization: mtx.
|
||||
*/
|
||||
extent_heap_t heaps[NPSIZES+1];
|
||||
|
||||
/*
|
||||
* Bitmap for which set bits correspond to non-empty heaps.
|
||||
*
|
||||
* Synchronization: mtx.
|
||||
*/
|
||||
bitmap_t bitmap[BITMAP_GROUPS(NPSIZES+1)];
|
||||
|
||||
/*
|
||||
* LRU of all extents in heaps.
|
||||
*
|
||||
* Synchronization: mtx.
|
||||
*/
|
||||
extent_list_t lru;
|
||||
|
||||
/*
|
||||
* Page sum for all extents in heaps.
|
||||
*
|
||||
* The synchronization here is a little tricky. Modifications to npages
|
||||
* must hold mtx, but reads need not (though, a reader who sees npages
|
||||
* without holding the mutex can't assume anything about the rest of the
|
||||
* state of the extents_t).
|
||||
*/
|
||||
atomic_zu_t npages;
|
||||
|
||||
/* All stored extents must be in the same state. */
|
||||
extent_state_t state;
|
||||
|
||||
/*
|
||||
* If true, delay coalescing until eviction; otherwise coalesce during
|
||||
* deallocation.
|
||||
*/
|
||||
bool delay_coalesce;
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_STRUCTS_H */
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTENT_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_EXTENT_TYPES_H
|
||||
|
||||
typedef struct extent_s extent_t;
|
||||
typedef struct extents_s extents_t;
|
||||
|
||||
#define EXTENT_HOOKS_INITIALIZER NULL
|
||||
|
||||
#define EXTENT_GROW_MAX_PIND (NPSIZES - 1)
|
||||
|
||||
/*
|
||||
* When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)
|
||||
* is the max ratio between the size of the active extent and the new extent.
|
||||
*/
|
||||
#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_TYPES_H */
|
||||
378
include/jemalloc/internal/fb.h
Normal file
378
include/jemalloc/internal/fb.h
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
#ifndef JEMALLOC_INTERNAL_FB_H
|
||||
#define JEMALLOC_INTERNAL_FB_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
|
||||
/*
|
||||
* The flat bitmap module. This has a larger API relative to the bitmap module
|
||||
* (supporting things like backwards searches, and searching for both set and
|
||||
* unset bits), at the cost of slower operations for very large bitmaps.
|
||||
*
|
||||
* Initialized flat bitmaps start at all-zeros (all bits unset).
|
||||
*/
|
||||
|
||||
typedef unsigned long fb_group_t;
|
||||
#define FB_GROUP_BITS (ZU(1) << (LG_SIZEOF_LONG + 3))
|
||||
#define FB_NGROUPS(nbits) \
|
||||
((nbits) / FB_GROUP_BITS + ((nbits) % FB_GROUP_BITS == 0 ? 0 : 1))
|
||||
|
||||
static inline void
|
||||
fb_init(fb_group_t *fb, size_t nbits) {
|
||||
size_t ngroups = FB_NGROUPS(nbits);
|
||||
memset(fb, 0, ngroups * sizeof(fb_group_t));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fb_empty(fb_group_t *fb, size_t nbits) {
|
||||
size_t ngroups = FB_NGROUPS(nbits);
|
||||
for (size_t i = 0; i < ngroups; i++) {
|
||||
if (fb[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fb_full(fb_group_t *fb, size_t nbits) {
|
||||
size_t ngroups = FB_NGROUPS(nbits);
|
||||
size_t trailing_bits = nbits % FB_GROUP_BITS;
|
||||
size_t limit = (trailing_bits == 0 ? ngroups : ngroups - 1);
|
||||
for (size_t i = 0; i < limit; i++) {
|
||||
if (fb[i] != ~(fb_group_t)0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (trailing_bits == 0) {
|
||||
return true;
|
||||
}
|
||||
return fb[ngroups - 1] == ((fb_group_t)1 << trailing_bits) - 1;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fb_get(fb_group_t *fb, size_t nbits, size_t bit) {
|
||||
assert(bit < nbits);
|
||||
size_t group_ind = bit / FB_GROUP_BITS;
|
||||
size_t bit_ind = bit % FB_GROUP_BITS;
|
||||
return (bool)(fb[group_ind] & ((fb_group_t)1 << bit_ind));
|
||||
}
|
||||
|
||||
static inline void
|
||||
fb_set(fb_group_t *fb, size_t nbits, size_t bit) {
|
||||
assert(bit < nbits);
|
||||
size_t group_ind = bit / FB_GROUP_BITS;
|
||||
size_t bit_ind = bit % FB_GROUP_BITS;
|
||||
fb[group_ind] |= ((fb_group_t)1 << bit_ind);
|
||||
}
|
||||
|
||||
static inline void
|
||||
fb_unset(fb_group_t *fb, size_t nbits, size_t bit) {
|
||||
assert(bit < nbits);
|
||||
size_t group_ind = bit / FB_GROUP_BITS;
|
||||
size_t bit_ind = bit % FB_GROUP_BITS;
|
||||
fb[group_ind] &= ~((fb_group_t)1 << bit_ind);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some implementation details. This visitation function lets us apply a group
|
||||
* visitor to each group in the bitmap (potentially modifying it). The mask
|
||||
* indicates which bits are logically part of the visitation.
|
||||
*/
|
||||
typedef void (*fb_group_visitor_t)(void *ctx, fb_group_t *fb, fb_group_t mask);
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
fb_visit_impl(fb_group_t *fb, size_t nbits, fb_group_visitor_t visit, void *ctx,
|
||||
size_t start, size_t cnt) {
|
||||
assert(cnt > 0);
|
||||
assert(start + cnt <= nbits);
|
||||
size_t group_ind = start / FB_GROUP_BITS;
|
||||
size_t start_bit_ind = start % FB_GROUP_BITS;
|
||||
/*
|
||||
* The first group is special; it's the only one we don't start writing
|
||||
* to from bit 0.
|
||||
*/
|
||||
size_t first_group_cnt = (start_bit_ind + cnt > FB_GROUP_BITS
|
||||
? FB_GROUP_BITS - start_bit_ind
|
||||
: cnt);
|
||||
/*
|
||||
* We can basically split affected words into:
|
||||
* - The first group, where we touch only the high bits
|
||||
* - The last group, where we touch only the low bits
|
||||
* - The middle, where we set all the bits to the same thing.
|
||||
* We treat each case individually. The last two could be merged, but
|
||||
* this can lead to bad codegen for those middle words.
|
||||
*/
|
||||
/* First group */
|
||||
fb_group_t mask =
|
||||
((~(fb_group_t)0) >> (FB_GROUP_BITS - first_group_cnt))
|
||||
<< start_bit_ind;
|
||||
visit(ctx, &fb[group_ind], mask);
|
||||
|
||||
cnt -= first_group_cnt;
|
||||
group_ind++;
|
||||
/* Middle groups */
|
||||
while (cnt > FB_GROUP_BITS) {
|
||||
visit(ctx, &fb[group_ind], ~(fb_group_t)0);
|
||||
cnt -= FB_GROUP_BITS;
|
||||
group_ind++;
|
||||
}
|
||||
/* Last group */
|
||||
if (cnt != 0) {
|
||||
mask = (~(fb_group_t)0) >> (FB_GROUP_BITS - cnt);
|
||||
visit(ctx, &fb[group_ind], mask);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
fb_assign_visitor(void *ctx, fb_group_t *fb, fb_group_t mask) {
|
||||
bool val = *(bool *)ctx;
|
||||
if (val) {
|
||||
*fb |= mask;
|
||||
} else {
|
||||
*fb &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets the cnt bits starting at position start. Must not have a 0 count. */
|
||||
static inline void
|
||||
fb_set_range(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
|
||||
bool val = true;
|
||||
fb_visit_impl(fb, nbits, &fb_assign_visitor, &val, start, cnt);
|
||||
}
|
||||
|
||||
/* Unsets the cnt bits starting at position start. Must not have a 0 count. */
|
||||
static inline void
|
||||
fb_unset_range(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
|
||||
bool val = false;
|
||||
fb_visit_impl(fb, nbits, &fb_assign_visitor, &val, start, cnt);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
fb_scount_visitor(void *ctx, fb_group_t *fb, fb_group_t mask) {
|
||||
size_t *scount = (size_t *)ctx;
|
||||
*scount += popcount_lu(*fb & mask);
|
||||
}
|
||||
|
||||
/* Finds the number of set bit in the of length cnt starting at start. */
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
fb_scount(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
|
||||
size_t scount = 0;
|
||||
fb_visit_impl(fb, nbits, &fb_scount_visitor, &scount, start, cnt);
|
||||
return scount;
|
||||
}
|
||||
|
||||
/* Finds the number of unset bit in the of length cnt starting at start. */
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
fb_ucount(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
|
||||
size_t scount = fb_scount(fb, nbits, start, cnt);
|
||||
return cnt - scount;
|
||||
}
|
||||
|
||||
/*
|
||||
* An implementation detail; find the first bit at position >= min_bit with the
|
||||
* value val.
|
||||
*
|
||||
* Returns the number of bits in the bitmap if no such bit exists.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE ssize_t
|
||||
fb_find_impl(
|
||||
fb_group_t *fb, size_t nbits, size_t start, bool val, bool forward) {
|
||||
assert(start < nbits);
|
||||
size_t ngroups = FB_NGROUPS(nbits);
|
||||
ssize_t group_ind = start / FB_GROUP_BITS;
|
||||
size_t bit_ind = start % FB_GROUP_BITS;
|
||||
|
||||
fb_group_t maybe_invert = (val ? 0 : (fb_group_t)-1);
|
||||
|
||||
fb_group_t group = fb[group_ind];
|
||||
group ^= maybe_invert;
|
||||
if (forward) {
|
||||
/* Only keep ones in bits bit_ind and above. */
|
||||
group &= ~((1LU << bit_ind) - 1);
|
||||
} else {
|
||||
/*
|
||||
* Only keep ones in bits bit_ind and below. You might more
|
||||
* naturally express this as (1 << (bit_ind + 1)) - 1, but
|
||||
* that shifts by an invalid amount if bit_ind is one less than
|
||||
* FB_GROUP_BITS.
|
||||
*/
|
||||
group &= ((2LU << bit_ind) - 1);
|
||||
}
|
||||
ssize_t group_ind_bound = forward ? (ssize_t)ngroups : -1;
|
||||
while (group == 0) {
|
||||
group_ind += forward ? 1 : -1;
|
||||
if (group_ind == group_ind_bound) {
|
||||
return forward ? (ssize_t)nbits : (ssize_t)-1;
|
||||
}
|
||||
group = fb[group_ind];
|
||||
group ^= maybe_invert;
|
||||
}
|
||||
assert(group != 0);
|
||||
size_t bit = forward ? ffs_lu(group) : fls_lu(group);
|
||||
size_t pos = group_ind * FB_GROUP_BITS + bit;
|
||||
/*
|
||||
* The high bits of a partially filled last group are zeros, so if we're
|
||||
* looking for zeros we don't want to report an invalid result.
|
||||
*/
|
||||
if (forward && !val && pos > nbits) {
|
||||
return nbits;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the first set bit in the bitmap with an index >= min_bit. Returns the
|
||||
* number of bits in the bitmap if no such bit exists.
|
||||
*/
|
||||
static inline size_t
|
||||
fb_ffu(fb_group_t *fb, size_t nbits, size_t min_bit) {
|
||||
return (size_t)fb_find_impl(fb, nbits, min_bit, /* val */ false,
|
||||
/* forward */ true);
|
||||
}
|
||||
|
||||
/* The same, but looks for an unset bit. */
|
||||
static inline size_t
|
||||
fb_ffs(fb_group_t *fb, size_t nbits, size_t min_bit) {
|
||||
return (size_t)fb_find_impl(fb, nbits, min_bit, /* val */ true,
|
||||
/* forward */ true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the last set bit in the bitmap with an index <= max_bit. Returns -1 if
|
||||
* no such bit exists.
|
||||
*/
|
||||
static inline ssize_t
|
||||
fb_flu(fb_group_t *fb, size_t nbits, size_t max_bit) {
|
||||
return fb_find_impl(fb, nbits, max_bit, /* val */ false,
|
||||
/* forward */ false);
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
fb_fls(fb_group_t *fb, size_t nbits, size_t max_bit) {
|
||||
return fb_find_impl(fb, nbits, max_bit, /* val */ true,
|
||||
/* forward */ false);
|
||||
}
|
||||
|
||||
/* Returns whether or not we found a range. */
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
fb_iter_range_impl(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
|
||||
size_t *r_len, bool val, bool forward) {
|
||||
assert(start < nbits);
|
||||
ssize_t next_range_begin = fb_find_impl(fb, nbits, start, val, forward);
|
||||
if ((forward && next_range_begin == (ssize_t)nbits)
|
||||
|| (!forward && next_range_begin == (ssize_t)-1)) {
|
||||
return false;
|
||||
}
|
||||
/* Half open range; the set bits are [begin, end). */
|
||||
ssize_t next_range_end = fb_find_impl(
|
||||
fb, nbits, next_range_begin, !val, forward);
|
||||
if (forward) {
|
||||
*r_begin = next_range_begin;
|
||||
*r_len = next_range_end - next_range_begin;
|
||||
} else {
|
||||
*r_begin = next_range_end + 1;
|
||||
*r_len = next_range_begin - next_range_end;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to iterate through ranges of set bits.
|
||||
*
|
||||
* Tries to find the next contiguous sequence of set bits with a first index >=
|
||||
* start. If one exists, puts the earliest bit of the range in *r_begin, its
|
||||
* length in *r_len, and returns true. Otherwise, returns false (without
|
||||
* touching *r_begin or *r_end).
|
||||
*/
|
||||
static inline bool
|
||||
fb_srange_iter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
|
||||
size_t *r_len) {
|
||||
return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
|
||||
/* val */ true, /* forward */ true);
|
||||
}
|
||||
|
||||
/*
|
||||
* The same as fb_srange_iter, but searches backwards from start rather than
|
||||
* forwards. (The position returned is still the earliest bit in the range).
|
||||
*/
|
||||
static inline bool
|
||||
fb_srange_riter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
|
||||
size_t *r_len) {
|
||||
return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
|
||||
/* val */ true, /* forward */ false);
|
||||
}
|
||||
|
||||
/* Similar to fb_srange_iter, but searches for unset bits. */
|
||||
static inline bool
|
||||
fb_urange_iter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
|
||||
size_t *r_len) {
|
||||
return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
|
||||
/* val */ false, /* forward */ true);
|
||||
}
|
||||
|
||||
/* Similar to fb_srange_riter, but searches for unset bits. */
|
||||
static inline bool
|
||||
fb_urange_riter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
|
||||
size_t *r_len) {
|
||||
return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
|
||||
/* val */ false, /* forward */ false);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
fb_range_longest_impl(fb_group_t *fb, size_t nbits, bool val) {
|
||||
size_t begin = 0;
|
||||
size_t longest_len = 0;
|
||||
size_t len = 0;
|
||||
while (begin < nbits
|
||||
&& fb_iter_range_impl(
|
||||
fb, nbits, begin, &begin, &len, val, /* forward */ true)) {
|
||||
if (len > longest_len) {
|
||||
longest_len = len;
|
||||
}
|
||||
begin += len;
|
||||
}
|
||||
return longest_len;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
fb_srange_longest(fb_group_t *fb, size_t nbits) {
|
||||
return fb_range_longest_impl(fb, nbits, /* val */ true);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
fb_urange_longest(fb_group_t *fb, size_t nbits) {
|
||||
return fb_range_longest_impl(fb, nbits, /* val */ false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes each bit of dst with the bitwise-AND of the corresponding bits of
|
||||
* src1 and src2. All bitmaps must be the same size.
|
||||
*/
|
||||
static inline void
|
||||
fb_bit_and(fb_group_t *dst, fb_group_t *src1, fb_group_t *src2, size_t nbits) {
|
||||
size_t ngroups = FB_NGROUPS(nbits);
|
||||
for (size_t i = 0; i < ngroups; i++) {
|
||||
dst[i] = src1[i] & src2[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Like fb_bit_and, but with bitwise-OR. */
|
||||
static inline void
|
||||
fb_bit_or(fb_group_t *dst, fb_group_t *src1, fb_group_t *src2, size_t nbits) {
|
||||
size_t ngroups = FB_NGROUPS(nbits);
|
||||
for (size_t i = 0; i < ngroups; i++) {
|
||||
dst[i] = src1[i] | src2[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Initializes dst bit i to the negation of source bit i. */
|
||||
static inline void
|
||||
fb_bit_not(fb_group_t *dst, fb_group_t *src, size_t nbits) {
|
||||
size_t ngroups = FB_NGROUPS(nbits);
|
||||
for (size_t i = 0; i < ngroups; i++) {
|
||||
dst[i] = ~src[i];
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_FB_H */
|
||||
129
include/jemalloc/internal/fxp.h
Normal file
129
include/jemalloc/internal/fxp.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#ifndef JEMALLOC_INTERNAL_FXP_H
|
||||
#define JEMALLOC_INTERNAL_FXP_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
|
||||
/*
|
||||
* A simple fixed-point math implementation, supporting only unsigned values
|
||||
* (with overflow being an error).
|
||||
*
|
||||
* It's not in general safe to use floating point in core code, because various
|
||||
* libc implementations we get linked against can assume that malloc won't touch
|
||||
* floating point state and call it with an unusual calling convention.
|
||||
*/
|
||||
|
||||
/*
|
||||
* High 16 bits are the integer part, low 16 are the fractional part. Or
|
||||
* equivalently, repr == 2**16 * val, where we use "val" to refer to the
|
||||
* (imaginary) fractional representation of the true value.
|
||||
*
|
||||
* We pick a uint32_t here since it's convenient in some places to
|
||||
* double the representation size (i.e. multiplication and division use
|
||||
* 64-bit integer types), and a uint64_t is the largest type we're
|
||||
* certain is available.
|
||||
*/
|
||||
typedef uint32_t fxp_t;
|
||||
#define FXP_INIT_INT(x) ((x) << 16)
|
||||
#define FXP_INIT_PERCENT(pct) (((pct) << 16) / 100)
|
||||
|
||||
/*
|
||||
* Amount of precision used in parsing and printing numbers. The integer bound
|
||||
* is simply because the integer part of the number gets 16 bits, and so is
|
||||
* bounded by 65536.
|
||||
*
|
||||
* We use a lot of precision for the fractional part, even though most of it
|
||||
* gets rounded off; this lets us get exact values for the important special
|
||||
* case where the denominator is a small power of 2 (for instance,
|
||||
* 1/512 == 0.001953125 is exactly representable even with only 16 bits of
|
||||
* fractional precision). We need to left-shift by 16 before dividing by
|
||||
* 10**precision, so we pick precision to be floor(log(2**48)) = 14.
|
||||
*/
|
||||
#define FXP_INTEGER_PART_DIGITS 5
|
||||
#define FXP_FRACTIONAL_PART_DIGITS 14
|
||||
|
||||
/*
|
||||
* In addition to the integer and fractional parts of the number, we need to
|
||||
* include a null character and (possibly) a decimal point.
|
||||
*/
|
||||
#define FXP_BUF_SIZE (FXP_INTEGER_PART_DIGITS + FXP_FRACTIONAL_PART_DIGITS + 2)
|
||||
|
||||
static inline fxp_t
|
||||
fxp_add(fxp_t a, fxp_t b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
static inline fxp_t
|
||||
fxp_sub(fxp_t a, fxp_t b) {
|
||||
assert(a >= b);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static inline fxp_t
|
||||
fxp_mul(fxp_t a, fxp_t b) {
|
||||
uint64_t unshifted = (uint64_t)a * (uint64_t)b;
|
||||
/*
|
||||
* Unshifted is (a.val * 2**16) * (b.val * 2**16)
|
||||
* == (a.val * b.val) * 2**32, but we want
|
||||
* (a.val * b.val) * 2 ** 16.
|
||||
*/
|
||||
return (uint32_t)(unshifted >> 16);
|
||||
}
|
||||
|
||||
static inline fxp_t
|
||||
fxp_div(fxp_t a, fxp_t b) {
|
||||
assert(b != 0);
|
||||
uint64_t unshifted = ((uint64_t)a << 32) / (uint64_t)b;
|
||||
/*
|
||||
* Unshifted is (a.val * 2**16) * (2**32) / (b.val * 2**16)
|
||||
* == (a.val / b.val) * (2 ** 32), which again corresponds to a right
|
||||
* shift of 16.
|
||||
*/
|
||||
return (uint32_t)(unshifted >> 16);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
fxp_round_down(fxp_t a) {
|
||||
return a >> 16;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
fxp_round_nearest(fxp_t a) {
|
||||
uint32_t fractional_part = (a & ((1U << 16) - 1));
|
||||
uint32_t increment = (uint32_t)(fractional_part >= (1U << 15));
|
||||
return (a >> 16) + increment;
|
||||
}
|
||||
|
||||
/*
|
||||
* Approximately computes x * frac, without the size limitations that would be
|
||||
* imposed by converting u to an fxp_t.
|
||||
*/
|
||||
static inline size_t
|
||||
fxp_mul_frac(size_t x_orig, fxp_t frac) {
|
||||
assert(frac <= (1U << 16));
|
||||
/*
|
||||
* Work around an over-enthusiastic warning about type limits below (on
|
||||
* 32-bit platforms, a size_t is always less than 1ULL << 48).
|
||||
*/
|
||||
uint64_t x = (uint64_t)x_orig;
|
||||
/*
|
||||
* If we can guarantee no overflow, multiply first before shifting, to
|
||||
* preserve some precision. Otherwise, shift first and then multiply.
|
||||
* In the latter case, we only lose the low 16 bits of a 48-bit number,
|
||||
* so we're still accurate to within 1/2**32.
|
||||
*/
|
||||
if (x < (1ULL << 48)) {
|
||||
return (size_t)((x * frac) >> 16);
|
||||
} else {
|
||||
return (size_t)((x >> 16) * (uint64_t)frac);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true on error. Otherwise, returns false and updates *ptr to point to
|
||||
* the first character not parsed (because it wasn't a digit).
|
||||
*/
|
||||
bool fxp_parse(fxp_t *a, const char *ptr, char **end);
|
||||
void fxp_print(fxp_t a, char buf[FXP_BUF_SIZE]);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_FXP_H */
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HASH_H
|
||||
#define JEMALLOC_INTERNAL_HASH_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
|
||||
/*
|
||||
|
|
@ -24,7 +25,7 @@ hash_rotl_64(uint64_t x, int8_t r) {
|
|||
static inline uint32_t
|
||||
hash_get_block_32(const uint32_t *p, int i) {
|
||||
/* Handle unaligned read. */
|
||||
if (unlikely((uintptr_t)p & (sizeof(uint32_t)-1)) != 0) {
|
||||
if (unlikely((uintptr_t)p & (sizeof(uint32_t) - 1)) != 0) {
|
||||
uint32_t ret;
|
||||
|
||||
memcpy(&ret, (uint8_t *)(p + i), sizeof(uint32_t));
|
||||
|
|
@ -37,7 +38,7 @@ hash_get_block_32(const uint32_t *p, int i) {
|
|||
static inline uint64_t
|
||||
hash_get_block_64(const uint64_t *p, int i) {
|
||||
/* Handle unaligned read. */
|
||||
if (unlikely((uintptr_t)p & (sizeof(uint64_t)-1)) != 0) {
|
||||
if (unlikely((uintptr_t)p & (sizeof(uint64_t) - 1)) != 0) {
|
||||
uint64_t ret;
|
||||
|
||||
memcpy(&ret, (uint8_t *)(p + i), sizeof(uint64_t));
|
||||
|
|
@ -71,8 +72,8 @@ hash_fmix_64(uint64_t k) {
|
|||
|
||||
static inline uint32_t
|
||||
hash_x86_32(const void *key, int len, uint32_t seed) {
|
||||
const uint8_t *data = (const uint8_t *) key;
|
||||
const int nblocks = len / 4;
|
||||
const uint8_t *data = (const uint8_t *)key;
|
||||
const int nblocks = len / 4;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
|
||||
|
|
@ -81,8 +82,8 @@ hash_x86_32(const void *key, int len, uint32_t seed) {
|
|||
|
||||
/* body */
|
||||
{
|
||||
const uint32_t *blocks = (const uint32_t *) (data + nblocks*4);
|
||||
int i;
|
||||
const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
|
||||
int i;
|
||||
|
||||
for (i = -nblocks; i; i++) {
|
||||
uint32_t k1 = hash_get_block_32(blocks, i);
|
||||
|
|
@ -93,21 +94,29 @@ hash_x86_32(const void *key, int len, uint32_t seed) {
|
|||
|
||||
h1 ^= k1;
|
||||
h1 = hash_rotl_32(h1, 13);
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
h1 = h1 * 5 + 0xe6546b64;
|
||||
}
|
||||
}
|
||||
|
||||
/* tail */
|
||||
{
|
||||
const uint8_t *tail = (const uint8_t *) (data + nblocks*4);
|
||||
const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
|
||||
switch (len & 3) {
|
||||
case 3: k1 ^= tail[2] << 16;
|
||||
case 2: k1 ^= tail[1] << 8;
|
||||
case 1: k1 ^= tail[0]; k1 *= c1; k1 = hash_rotl_32(k1, 15);
|
||||
k1 *= c2; h1 ^= k1;
|
||||
case 3:
|
||||
k1 ^= tail[2] << 16;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 2:
|
||||
k1 ^= tail[1] << 8;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 1:
|
||||
k1 ^= tail[0];
|
||||
k1 *= c1;
|
||||
k1 = hash_rotl_32(k1, 15);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,11 +128,10 @@ hash_x86_32(const void *key, int len, uint32_t seed) {
|
|||
return h1;
|
||||
}
|
||||
|
||||
UNUSED static inline void
|
||||
hash_x86_128(const void *key, const int len, uint32_t seed,
|
||||
uint64_t r_out[2]) {
|
||||
const uint8_t * data = (const uint8_t *) key;
|
||||
const int nblocks = len / 16;
|
||||
static inline void
|
||||
hash_x86_128(const void *key, const int len, uint32_t seed, uint64_t r_out[2]) {
|
||||
const uint8_t *data = (const uint8_t *)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
uint32_t h2 = seed;
|
||||
|
|
@ -137,94 +145,161 @@ hash_x86_128(const void *key, const int len, uint32_t seed,
|
|||
|
||||
/* body */
|
||||
{
|
||||
const uint32_t *blocks = (const uint32_t *) (data + nblocks*16);
|
||||
int i;
|
||||
const uint32_t *blocks = (const uint32_t *)(data
|
||||
+ nblocks * 16);
|
||||
int i;
|
||||
|
||||
for (i = -nblocks; i; i++) {
|
||||
uint32_t k1 = hash_get_block_32(blocks, i*4 + 0);
|
||||
uint32_t k2 = hash_get_block_32(blocks, i*4 + 1);
|
||||
uint32_t k3 = hash_get_block_32(blocks, i*4 + 2);
|
||||
uint32_t k4 = hash_get_block_32(blocks, i*4 + 3);
|
||||
uint32_t k1 = hash_get_block_32(blocks, i * 4 + 0);
|
||||
uint32_t k2 = hash_get_block_32(blocks, i * 4 + 1);
|
||||
uint32_t k3 = hash_get_block_32(blocks, i * 4 + 2);
|
||||
uint32_t k4 = hash_get_block_32(blocks, i * 4 + 3);
|
||||
|
||||
k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
|
||||
k1 *= c1;
|
||||
k1 = hash_rotl_32(k1, 15);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
|
||||
h1 = hash_rotl_32(h1, 19); h1 += h2;
|
||||
h1 = h1*5 + 0x561ccd1b;
|
||||
h1 = hash_rotl_32(h1, 19);
|
||||
h1 += h2;
|
||||
h1 = h1 * 5 + 0x561ccd1b;
|
||||
|
||||
k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
|
||||
k2 *= c2;
|
||||
k2 = hash_rotl_32(k2, 16);
|
||||
k2 *= c3;
|
||||
h2 ^= k2;
|
||||
|
||||
h2 = hash_rotl_32(h2, 17); h2 += h3;
|
||||
h2 = h2*5 + 0x0bcaa747;
|
||||
h2 = hash_rotl_32(h2, 17);
|
||||
h2 += h3;
|
||||
h2 = h2 * 5 + 0x0bcaa747;
|
||||
|
||||
k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
|
||||
k3 *= c3;
|
||||
k3 = hash_rotl_32(k3, 17);
|
||||
k3 *= c4;
|
||||
h3 ^= k3;
|
||||
|
||||
h3 = hash_rotl_32(h3, 15); h3 += h4;
|
||||
h3 = h3*5 + 0x96cd1c35;
|
||||
h3 = hash_rotl_32(h3, 15);
|
||||
h3 += h4;
|
||||
h3 = h3 * 5 + 0x96cd1c35;
|
||||
|
||||
k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
|
||||
k4 *= c4;
|
||||
k4 = hash_rotl_32(k4, 18);
|
||||
k4 *= c1;
|
||||
h4 ^= k4;
|
||||
|
||||
h4 = hash_rotl_32(h4, 13); h4 += h1;
|
||||
h4 = h4*5 + 0x32ac3b17;
|
||||
h4 = hash_rotl_32(h4, 13);
|
||||
h4 += h1;
|
||||
h4 = h4 * 5 + 0x32ac3b17;
|
||||
}
|
||||
}
|
||||
|
||||
/* tail */
|
||||
{
|
||||
const uint8_t *tail = (const uint8_t *) (data + nblocks*16);
|
||||
uint32_t k1 = 0;
|
||||
uint32_t k2 = 0;
|
||||
uint32_t k3 = 0;
|
||||
uint32_t k4 = 0;
|
||||
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
|
||||
uint32_t k1 = 0;
|
||||
uint32_t k2 = 0;
|
||||
uint32_t k3 = 0;
|
||||
uint32_t k4 = 0;
|
||||
|
||||
switch (len & 15) {
|
||||
case 15: k4 ^= tail[14] << 16;
|
||||
case 14: k4 ^= tail[13] << 8;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
case 12: k3 ^= tail[11] << 24;
|
||||
case 11: k3 ^= tail[10] << 16;
|
||||
case 10: k3 ^= tail[ 9] << 8;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
case 8: k2 ^= tail[ 7] << 24;
|
||||
case 7: k2 ^= tail[ 6] << 16;
|
||||
case 6: k2 ^= tail[ 5] << 8;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
case 4: k1 ^= tail[ 3] << 24;
|
||||
case 3: k1 ^= tail[ 2] << 16;
|
||||
case 2: k1 ^= tail[ 1] << 8;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
|
||||
case 15:
|
||||
k4 ^= tail[14] << 16;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 14:
|
||||
k4 ^= tail[13] << 8;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 13:
|
||||
k4 ^= tail[12] << 0;
|
||||
k4 *= c4;
|
||||
k4 = hash_rotl_32(k4, 18);
|
||||
k4 *= c1;
|
||||
h4 ^= k4;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 12:
|
||||
k3 ^= (uint32_t)tail[11] << 24;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 11:
|
||||
k3 ^= tail[10] << 16;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 10:
|
||||
k3 ^= tail[9] << 8;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 9:
|
||||
k3 ^= tail[8] << 0;
|
||||
k3 *= c3;
|
||||
k3 = hash_rotl_32(k3, 17);
|
||||
k3 *= c4;
|
||||
h3 ^= k3;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 8:
|
||||
k2 ^= (uint32_t)tail[7] << 24;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 7:
|
||||
k2 ^= tail[6] << 16;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 6:
|
||||
k2 ^= tail[5] << 8;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 5:
|
||||
k2 ^= tail[4] << 0;
|
||||
k2 *= c2;
|
||||
k2 = hash_rotl_32(k2, 16);
|
||||
k2 *= c3;
|
||||
h2 ^= k2;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 4:
|
||||
k1 ^= (uint32_t)tail[3] << 24;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 3:
|
||||
k1 ^= tail[2] << 16;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 2:
|
||||
k1 ^= tail[1] << 8;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 1:
|
||||
k1 ^= tail[0] << 0;
|
||||
k1 *= c1;
|
||||
k1 = hash_rotl_32(k1, 15);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* finalization */
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
h1 ^= len;
|
||||
h2 ^= len;
|
||||
h3 ^= len;
|
||||
h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
h1 += h2;
|
||||
h1 += h3;
|
||||
h1 += h4;
|
||||
h2 += h1;
|
||||
h3 += h1;
|
||||
h4 += h1;
|
||||
|
||||
h1 = hash_fmix_32(h1);
|
||||
h2 = hash_fmix_32(h2);
|
||||
h3 = hash_fmix_32(h3);
|
||||
h4 = hash_fmix_32(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
h1 += h2;
|
||||
h1 += h3;
|
||||
h1 += h4;
|
||||
h2 += h1;
|
||||
h3 += h1;
|
||||
h4 += h1;
|
||||
|
||||
r_out[0] = (((uint64_t) h2) << 32) | h1;
|
||||
r_out[1] = (((uint64_t) h4) << 32) | h3;
|
||||
r_out[0] = (((uint64_t)h2) << 32) | h1;
|
||||
r_out[1] = (((uint64_t)h4) << 32) | h3;
|
||||
}
|
||||
|
||||
UNUSED static inline void
|
||||
hash_x64_128(const void *key, const int len, const uint32_t seed,
|
||||
uint64_t r_out[2]) {
|
||||
const uint8_t *data = (const uint8_t *) key;
|
||||
const int nblocks = len / 16;
|
||||
static inline void
|
||||
hash_x64_128(
|
||||
const void *key, const int len, const uint32_t seed, uint64_t r_out[2]) {
|
||||
const uint8_t *data = (const uint8_t *)key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint64_t h1 = seed;
|
||||
uint64_t h2 = seed;
|
||||
|
|
@ -234,55 +309,99 @@ hash_x64_128(const void *key, const int len, const uint32_t seed,
|
|||
|
||||
/* body */
|
||||
{
|
||||
const uint64_t *blocks = (const uint64_t *) (data);
|
||||
int i;
|
||||
const uint64_t *blocks = (const uint64_t *)(data);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nblocks; i++) {
|
||||
uint64_t k1 = hash_get_block_64(blocks, i*2 + 0);
|
||||
uint64_t k2 = hash_get_block_64(blocks, i*2 + 1);
|
||||
uint64_t k1 = hash_get_block_64(blocks, i * 2 + 0);
|
||||
uint64_t k2 = hash_get_block_64(blocks, i * 2 + 1);
|
||||
|
||||
k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
|
||||
k1 *= c1;
|
||||
k1 = hash_rotl_64(k1, 31);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
|
||||
h1 = hash_rotl_64(h1, 27); h1 += h2;
|
||||
h1 = h1*5 + 0x52dce729;
|
||||
h1 = hash_rotl_64(h1, 27);
|
||||
h1 += h2;
|
||||
h1 = h1 * 5 + 0x52dce729;
|
||||
|
||||
k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
|
||||
k2 *= c2;
|
||||
k2 = hash_rotl_64(k2, 33);
|
||||
k2 *= c1;
|
||||
h2 ^= k2;
|
||||
|
||||
h2 = hash_rotl_64(h2, 31); h2 += h1;
|
||||
h2 = h2*5 + 0x38495ab5;
|
||||
h2 = hash_rotl_64(h2, 31);
|
||||
h2 += h1;
|
||||
h2 = h2 * 5 + 0x38495ab5;
|
||||
}
|
||||
}
|
||||
|
||||
/* tail */
|
||||
{
|
||||
const uint8_t *tail = (const uint8_t*)(data + nblocks*16);
|
||||
uint64_t k1 = 0;
|
||||
uint64_t k2 = 0;
|
||||
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
|
||||
uint64_t k1 = 0;
|
||||
uint64_t k2 = 0;
|
||||
|
||||
switch (len & 15) {
|
||||
case 15: k2 ^= ((uint64_t)(tail[14])) << 48; /* falls through */
|
||||
case 14: k2 ^= ((uint64_t)(tail[13])) << 40; /* falls through */
|
||||
case 13: k2 ^= ((uint64_t)(tail[12])) << 32; /* falls through */
|
||||
case 12: k2 ^= ((uint64_t)(tail[11])) << 24; /* falls through */
|
||||
case 11: k2 ^= ((uint64_t)(tail[10])) << 16; /* falls through */
|
||||
case 10: k2 ^= ((uint64_t)(tail[ 9])) << 8; /* falls through */
|
||||
case 9: k2 ^= ((uint64_t)(tail[ 8])) << 0;
|
||||
k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
|
||||
/* falls through */
|
||||
case 8: k1 ^= ((uint64_t)(tail[ 7])) << 56; /* falls through */
|
||||
case 7: k1 ^= ((uint64_t)(tail[ 6])) << 48; /* falls through */
|
||||
case 6: k1 ^= ((uint64_t)(tail[ 5])) << 40; /* falls through */
|
||||
case 5: k1 ^= ((uint64_t)(tail[ 4])) << 32; /* falls through */
|
||||
case 4: k1 ^= ((uint64_t)(tail[ 3])) << 24; /* falls through */
|
||||
case 3: k1 ^= ((uint64_t)(tail[ 2])) << 16; /* falls through */
|
||||
case 2: k1 ^= ((uint64_t)(tail[ 1])) << 8; /* falls through */
|
||||
case 1: k1 ^= ((uint64_t)(tail[ 0])) << 0;
|
||||
k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
|
||||
case 15:
|
||||
k2 ^= ((uint64_t)(tail[14])) << 48;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 14:
|
||||
k2 ^= ((uint64_t)(tail[13])) << 40;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 13:
|
||||
k2 ^= ((uint64_t)(tail[12])) << 32;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 12:
|
||||
k2 ^= ((uint64_t)(tail[11])) << 24;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 11:
|
||||
k2 ^= ((uint64_t)(tail[10])) << 16;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 10:
|
||||
k2 ^= ((uint64_t)(tail[9])) << 8;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 9:
|
||||
k2 ^= ((uint64_t)(tail[8])) << 0;
|
||||
k2 *= c2;
|
||||
k2 = hash_rotl_64(k2, 33);
|
||||
k2 *= c1;
|
||||
h2 ^= k2;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 8:
|
||||
k1 ^= ((uint64_t)(tail[7])) << 56;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 7:
|
||||
k1 ^= ((uint64_t)(tail[6])) << 48;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 6:
|
||||
k1 ^= ((uint64_t)(tail[5])) << 40;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 5:
|
||||
k1 ^= ((uint64_t)(tail[4])) << 32;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 4:
|
||||
k1 ^= ((uint64_t)(tail[3])) << 24;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 3:
|
||||
k1 ^= ((uint64_t)(tail[2])) << 16;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 2:
|
||||
k1 ^= ((uint64_t)(tail[1])) << 8;
|
||||
JEMALLOC_FALLTHROUGH;
|
||||
case 1:
|
||||
k1 ^= ((uint64_t)(tail[0])) << 0;
|
||||
k1 *= c1;
|
||||
k1 = hash_rotl_64(k1, 31);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* finalization */
|
||||
h1 ^= len; h2 ^= len;
|
||||
h1 ^= len;
|
||||
h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
|
|
|||
163
include/jemalloc/internal/hook.h
Normal file
163
include/jemalloc/internal/hook.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HOOK_H
|
||||
#define JEMALLOC_INTERNAL_HOOK_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/tsd.h"
|
||||
|
||||
/*
|
||||
* This API is *extremely* experimental, and may get ripped out, changed in API-
|
||||
* and ABI-incompatible ways, be insufficiently or incorrectly documented, etc.
|
||||
*
|
||||
* It allows hooking the stateful parts of the API to see changes as they
|
||||
* happen.
|
||||
*
|
||||
* Allocation hooks are called after the allocation is done, free hooks are
|
||||
* called before the free is done, and expand hooks are called after the
|
||||
* allocation is expanded.
|
||||
*
|
||||
* For realloc and rallocx, if the expansion happens in place, the expansion
|
||||
* hook is called. If it is moved, then the alloc hook is called on the new
|
||||
* location, and then the free hook is called on the old location (i.e. both
|
||||
* hooks are invoked in between the alloc and the dalloc).
|
||||
*
|
||||
* If we return NULL from OOM, then usize might not be trustworthy. Calling
|
||||
* realloc(NULL, size) only calls the alloc hook, and calling realloc(ptr, 0)
|
||||
* only calls the free hook. (Calling realloc(NULL, 0) is treated as malloc(0),
|
||||
* and only calls the alloc hook).
|
||||
*
|
||||
* Reentrancy:
|
||||
* Reentrancy is guarded against from within the hook implementation. If you
|
||||
* call allocator functions from within a hook, the hooks will not be invoked
|
||||
* again.
|
||||
* Threading:
|
||||
* The installation of a hook synchronizes with all its uses. If you can
|
||||
* prove the installation of a hook happens-before a jemalloc entry point,
|
||||
* then the hook will get invoked (unless there's a racing removal).
|
||||
*
|
||||
* Hook insertion appears to be atomic at a per-thread level (i.e. if a thread
|
||||
* allocates and has the alloc hook invoked, then a subsequent free on the
|
||||
* same thread will also have the free hook invoked).
|
||||
*
|
||||
* The *removal* of a hook does *not* block until all threads are done with
|
||||
* the hook. Hook authors have to be resilient to this, and need some
|
||||
* out-of-band mechanism for cleaning up any dynamically allocated memory
|
||||
* associated with their hook.
|
||||
* Ordering:
|
||||
* Order of hook execution is unspecified, and may be different than insertion
|
||||
* order.
|
||||
*/
|
||||
|
||||
#define HOOK_MAX 4
|
||||
|
||||
enum hook_alloc_e {
|
||||
hook_alloc_malloc,
|
||||
hook_alloc_posix_memalign,
|
||||
hook_alloc_aligned_alloc,
|
||||
hook_alloc_calloc,
|
||||
hook_alloc_memalign,
|
||||
hook_alloc_valloc,
|
||||
hook_alloc_pvalloc,
|
||||
hook_alloc_mallocx,
|
||||
|
||||
/* The reallocating functions have both alloc and dalloc variants */
|
||||
hook_alloc_realloc,
|
||||
hook_alloc_rallocx,
|
||||
};
|
||||
/*
|
||||
* We put the enum typedef after the enum, since this file may get included by
|
||||
* jemalloc_cpp.cpp, and C++ disallows enum forward declarations.
|
||||
*/
|
||||
typedef enum hook_alloc_e hook_alloc_t;
|
||||
|
||||
enum hook_dalloc_e {
|
||||
hook_dalloc_free,
|
||||
hook_dalloc_dallocx,
|
||||
hook_dalloc_sdallocx,
|
||||
|
||||
/*
|
||||
* The dalloc halves of reallocation (not called if in-place expansion
|
||||
* happens).
|
||||
*/
|
||||
hook_dalloc_realloc,
|
||||
hook_dalloc_rallocx,
|
||||
};
|
||||
typedef enum hook_dalloc_e hook_dalloc_t;
|
||||
|
||||
enum hook_expand_e {
|
||||
hook_expand_realloc,
|
||||
hook_expand_rallocx,
|
||||
hook_expand_xallocx,
|
||||
};
|
||||
typedef enum hook_expand_e hook_expand_t;
|
||||
|
||||
typedef void (*hook_alloc)(void *extra, hook_alloc_t type, void *result,
|
||||
uintptr_t result_raw, uintptr_t args_raw[3]);
|
||||
|
||||
typedef void (*hook_dalloc)(
|
||||
void *extra, hook_dalloc_t type, void *address, uintptr_t args_raw[3]);
|
||||
|
||||
typedef void (*hook_expand)(void *extra, hook_expand_t type, void *address,
|
||||
size_t old_usize, size_t new_usize, uintptr_t result_raw,
|
||||
uintptr_t args_raw[4]);
|
||||
|
||||
typedef struct hooks_s hooks_t;
|
||||
struct hooks_s {
|
||||
hook_alloc alloc_hook;
|
||||
hook_dalloc dalloc_hook;
|
||||
hook_expand expand_hook;
|
||||
void *extra;
|
||||
};
|
||||
|
||||
/*
|
||||
* Begin implementation details; everything above this point might one day live
|
||||
* in a public API. Everything below this point never will.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The realloc pathways haven't gotten any refactoring love in a while, and it's
|
||||
* fairly difficult to pass information from the entry point to the hooks. We
|
||||
* put the informaiton the hooks will need into a struct to encapsulate
|
||||
* everything.
|
||||
*
|
||||
* Much of these pathways are force-inlined, so that the compiler can avoid
|
||||
* materializing this struct until we hit an extern arena function. For fairly
|
||||
* goofy reasons, *many* of the realloc paths hit an extern arena function.
|
||||
* These paths are cold enough that it doesn't matter; eventually, we should
|
||||
* rewrite the realloc code to make the expand-in-place and the
|
||||
* free-then-realloc paths more orthogonal, at which point we don't need to
|
||||
* spread the hook logic all over the place.
|
||||
*/
|
||||
typedef struct hook_ralloc_args_s hook_ralloc_args_t;
|
||||
struct hook_ralloc_args_s {
|
||||
/* I.e. as opposed to rallocx. */
|
||||
bool is_realloc;
|
||||
/*
|
||||
* The expand hook takes 4 arguments, even if only 3 are actually used;
|
||||
* we add an extra one in case the user decides to memcpy without
|
||||
* looking too closely at the hooked function.
|
||||
*/
|
||||
uintptr_t args[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns an opaque handle to be used when removing the hook. NULL means that
|
||||
* we couldn't install the hook.
|
||||
*/
|
||||
bool hook_boot(void);
|
||||
|
||||
void *hook_install(tsdn_t *tsdn, hooks_t *to_install);
|
||||
/* Uninstalls the hook with the handle previously returned from hook_install. */
|
||||
void hook_remove(tsdn_t *tsdn, void *opaque);
|
||||
|
||||
/* Hooks */
|
||||
|
||||
void hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
|
||||
uintptr_t args_raw[3]);
|
||||
|
||||
void hook_invoke_dalloc(
|
||||
hook_dalloc_t type, void *address, uintptr_t args_raw[3]);
|
||||
|
||||
void hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
|
||||
size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HOOK_H */
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HOOKS_H
|
||||
#define JEMALLOC_INTERNAL_HOOKS_H
|
||||
|
||||
extern JEMALLOC_EXPORT void (*hooks_arena_new_hook)();
|
||||
extern JEMALLOC_EXPORT void (*hooks_libc_hook)();
|
||||
|
||||
#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
|
||||
|
||||
#define open JEMALLOC_HOOK(open, hooks_libc_hook)
|
||||
#define read JEMALLOC_HOOK(read, hooks_libc_hook)
|
||||
#define write JEMALLOC_HOOK(write, hooks_libc_hook)
|
||||
#define readlink JEMALLOC_HOOK(readlink, hooks_libc_hook)
|
||||
#define close JEMALLOC_HOOK(close, hooks_libc_hook)
|
||||
#define creat JEMALLOC_HOOK(creat, hooks_libc_hook)
|
||||
#define secure_getenv JEMALLOC_HOOK(secure_getenv, hooks_libc_hook)
|
||||
/* Note that this is undef'd and re-define'd in src/prof.c. */
|
||||
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HOOKS_H */
|
||||
185
include/jemalloc/internal/hpa.h
Normal file
185
include/jemalloc/internal/hpa.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HPA_H
|
||||
#define JEMALLOC_INTERNAL_HPA_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/base.h"
|
||||
#include "jemalloc/internal/edata_cache.h"
|
||||
#include "jemalloc/internal/emap.h"
|
||||
#include "jemalloc/internal/exp_grow.h"
|
||||
#include "jemalloc/internal/hpa_central.h"
|
||||
#include "jemalloc/internal/hpa_hooks.h"
|
||||
#include "jemalloc/internal/hpa_opts.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/pai.h"
|
||||
#include "jemalloc/internal/psset.h"
|
||||
#include "jemalloc/internal/sec.h"
|
||||
|
||||
typedef struct hpa_shard_nonderived_stats_s hpa_shard_nonderived_stats_t;
|
||||
struct hpa_shard_nonderived_stats_s {
|
||||
/*
|
||||
* The number of times we've purged within a hugepage.
|
||||
*
|
||||
* Guarded by mtx.
|
||||
*/
|
||||
uint64_t npurge_passes;
|
||||
/*
|
||||
* The number of individual purge calls we perform (which should always
|
||||
* be bigger than npurge_passes, since each pass purges at least one
|
||||
* extent within a hugepage.
|
||||
*
|
||||
* Guarded by mtx.
|
||||
*/
|
||||
uint64_t npurges;
|
||||
|
||||
/*
|
||||
* The number of times we've hugified a pageslab.
|
||||
*
|
||||
* Guarded by mtx.
|
||||
*/
|
||||
uint64_t nhugifies;
|
||||
|
||||
/*
|
||||
* The number of times we've tried to hugify a pageslab, but failed.
|
||||
*
|
||||
* Guarded by mtx.
|
||||
*/
|
||||
uint64_t nhugify_failures;
|
||||
|
||||
/*
|
||||
* The number of times we've dehugified a pageslab.
|
||||
*
|
||||
* Guarded by mtx.
|
||||
*/
|
||||
uint64_t ndehugifies;
|
||||
};
|
||||
|
||||
/* Completely derived; only used by CTL. */
|
||||
typedef struct hpa_shard_stats_s hpa_shard_stats_t;
|
||||
struct hpa_shard_stats_s {
|
||||
psset_stats_t psset_stats;
|
||||
hpa_shard_nonderived_stats_t nonderived_stats;
|
||||
sec_stats_t secstats;
|
||||
};
|
||||
|
||||
typedef struct hpa_shard_s hpa_shard_t;
|
||||
struct hpa_shard_s {
|
||||
/*
|
||||
* pai must be the first member; we cast from a pointer to it to a
|
||||
* pointer to the hpa_shard_t.
|
||||
*/
|
||||
pai_t pai;
|
||||
|
||||
/* The central allocator we get our hugepages from. */
|
||||
hpa_central_t *central;
|
||||
|
||||
/* Protects most of this shard's state. */
|
||||
malloc_mutex_t mtx;
|
||||
|
||||
/*
|
||||
* Guards the shard's access to the central allocator (preventing
|
||||
* multiple threads operating on this shard from accessing the central
|
||||
* allocator).
|
||||
*/
|
||||
malloc_mutex_t grow_mtx;
|
||||
|
||||
/* The base metadata allocator. */
|
||||
base_t *base;
|
||||
|
||||
/*
|
||||
* This edata cache is the one we use when allocating a small extent
|
||||
* from a pageslab. The pageslab itself comes from the centralized
|
||||
* allocator, and so will use its edata_cache.
|
||||
*/
|
||||
edata_cache_fast_t ecf;
|
||||
|
||||
/* Small extent cache (not guarded by mtx) */
|
||||
JEMALLOC_ALIGNED(CACHELINE) sec_t sec;
|
||||
|
||||
psset_t psset;
|
||||
|
||||
/*
|
||||
* How many grow operations have occurred.
|
||||
*
|
||||
* Guarded by grow_mtx.
|
||||
*/
|
||||
uint64_t age_counter;
|
||||
|
||||
/* The arena ind we're associated with. */
|
||||
unsigned ind;
|
||||
|
||||
/*
|
||||
* Our emap. This is just a cache of the emap pointer in the associated
|
||||
* hpa_central.
|
||||
*/
|
||||
emap_t *emap;
|
||||
|
||||
/* The configuration choices for this hpa shard. */
|
||||
hpa_shard_opts_t opts;
|
||||
|
||||
/*
|
||||
* How many pages have we started but not yet finished purging in this
|
||||
* hpa shard.
|
||||
*/
|
||||
size_t npending_purge;
|
||||
|
||||
/*
|
||||
* Those stats which are copied directly into the CTL-centric hpa shard
|
||||
* stats.
|
||||
*/
|
||||
hpa_shard_nonderived_stats_t stats;
|
||||
|
||||
/*
|
||||
* Last time we performed purge on this shard.
|
||||
*/
|
||||
nstime_t last_purge;
|
||||
|
||||
/*
|
||||
* Last time when we attempted work (purging or hugifying). If deferral
|
||||
* of the work is allowed (we have background thread), this is the time
|
||||
* when background thread checked if purging or hugifying needs to be
|
||||
* done. If deferral is not allowed, this is the time of (hpa_alloc or
|
||||
* hpa_dalloc) activity in the shard.
|
||||
*/
|
||||
nstime_t last_time_work_attempted;
|
||||
};
|
||||
|
||||
bool hpa_hugepage_size_exceeds_limit(void);
|
||||
/*
|
||||
* Whether or not the HPA can be used given the current configuration. This is
|
||||
* is not necessarily a guarantee that it backs its allocations by hugepages,
|
||||
* just that it can function properly given the system it's running on.
|
||||
*/
|
||||
bool hpa_supported(void);
|
||||
bool hpa_shard_init(tsdn_t *tsdn, hpa_shard_t *shard, hpa_central_t *central,
|
||||
emap_t *emap, base_t *base, edata_cache_t *edata_cache, unsigned ind,
|
||||
const hpa_shard_opts_t *opts, const sec_opts_t *sec_opts);
|
||||
|
||||
void hpa_shard_stats_accum(hpa_shard_stats_t *dst, hpa_shard_stats_t *src);
|
||||
void hpa_shard_stats_merge(
|
||||
tsdn_t *tsdn, hpa_shard_t *shard, hpa_shard_stats_t *dst);
|
||||
|
||||
/*
|
||||
* Notify the shard that we won't use it for allocations much longer. Due to
|
||||
* the possibility of races, we don't actually prevent allocations; just flush
|
||||
* and disable the embedded edata_cache_small.
|
||||
*/
|
||||
void hpa_shard_disable(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
void hpa_shard_destroy(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
/* Flush caches that shard may be using */
|
||||
void hpa_shard_flush(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
|
||||
void hpa_shard_set_deferral_allowed(
|
||||
tsdn_t *tsdn, hpa_shard_t *shard, bool deferral_allowed);
|
||||
void hpa_shard_do_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
|
||||
/*
|
||||
* We share the fork ordering with the PA and arena prefork handling; that's why
|
||||
* these are 2, 3 and 4 rather than 0 and 1.
|
||||
*/
|
||||
void hpa_shard_prefork2(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
void hpa_shard_prefork3(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
void hpa_shard_prefork4(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
void hpa_shard_postfork_parent(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
void hpa_shard_postfork_child(tsdn_t *tsdn, hpa_shard_t *shard);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HPA_H */
|
||||
41
include/jemalloc/internal/hpa_central.h
Normal file
41
include/jemalloc/internal/hpa_central.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HPA_CENTRAL_H
|
||||
#define JEMALLOC_INTERNAL_HPA_CENTRAL_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/base.h"
|
||||
#include "jemalloc/internal/hpa_hooks.h"
|
||||
#include "jemalloc/internal/hpdata.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
typedef struct hpa_central_s hpa_central_t;
|
||||
struct hpa_central_s {
|
||||
/*
|
||||
* Guards expansion of eden. We separate this from the regular mutex so
|
||||
* that cheaper operations can still continue while we're doing the OS
|
||||
* call.
|
||||
*/
|
||||
malloc_mutex_t grow_mtx;
|
||||
/*
|
||||
* Either NULL (if empty), or some integer multiple of a
|
||||
* hugepage-aligned number of hugepages. We carve them off one at a
|
||||
* time to satisfy new pageslab requests.
|
||||
*
|
||||
* Guarded by grow_mtx.
|
||||
*/
|
||||
void *eden;
|
||||
size_t eden_len;
|
||||
/* Source for metadata. */
|
||||
base_t *base;
|
||||
|
||||
/* The HPA hooks. */
|
||||
hpa_hooks_t hooks;
|
||||
};
|
||||
|
||||
bool hpa_central_init(
|
||||
hpa_central_t *central, base_t *base, const hpa_hooks_t *hooks);
|
||||
|
||||
hpdata_t *hpa_central_extract(tsdn_t *tsdn, hpa_central_t *central, size_t size,
|
||||
uint64_t age, bool hugify_eager, bool *oom);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HPA_CENTRAL_H */
|
||||
21
include/jemalloc/internal/hpa_hooks.h
Normal file
21
include/jemalloc/internal/hpa_hooks.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HPA_HOOKS_H
|
||||
#define JEMALLOC_INTERNAL_HPA_HOOKS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/nstime.h"
|
||||
|
||||
typedef struct hpa_hooks_s hpa_hooks_t;
|
||||
struct hpa_hooks_s {
|
||||
void *(*map)(size_t size);
|
||||
void (*unmap)(void *ptr, size_t size);
|
||||
void (*purge)(void *ptr, size_t size);
|
||||
bool (*hugify)(void *ptr, size_t size, bool sync);
|
||||
void (*dehugify)(void *ptr, size_t size);
|
||||
void (*curtime)(nstime_t *r_time, bool first_reading);
|
||||
uint64_t (*ms_since)(nstime_t *r_time);
|
||||
bool (*vectorized_purge)(void *vec, size_t vlen, size_t nbytes);
|
||||
};
|
||||
|
||||
extern const hpa_hooks_t hpa_hooks_default;
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HPA_HOOKS_H */
|
||||
190
include/jemalloc/internal/hpa_opts.h
Normal file
190
include/jemalloc/internal/hpa_opts.h
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HPA_OPTS_H
|
||||
#define JEMALLOC_INTERNAL_HPA_OPTS_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/fxp.h"
|
||||
|
||||
/*
|
||||
* This file is morally part of hpa.h, but is split out for header-ordering
|
||||
* reasons.
|
||||
*
|
||||
* All of these hpa_shard_opts below are experimental. We are exploring more
|
||||
* efficient packing, hugifying, and purging approaches to make efficient
|
||||
* trade-offs between CPU, memory, latency, and usability. This means all of
|
||||
* them are at the risk of being deprecated and corresponding configurations
|
||||
* should be updated once the final version settles.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This enum controls how jemalloc hugifies/dehugifies pages. Each style may be
|
||||
* more suitable depending on deployment environments.
|
||||
*
|
||||
* hpa_hugify_style_none
|
||||
* Using this means that jemalloc will not be hugifying or dehugifying pages,
|
||||
* but will let the kernel make those decisions. This style only makes sense
|
||||
* when deploying on systems where THP are enabled in 'always' mode. With this
|
||||
* style, you most likely want to have no purging at all (dirty_mult=-1) or
|
||||
* purge_threshold=HUGEPAGE bytes (2097152 for 2Mb page), although other
|
||||
* thresholds may work well depending on kernel settings of your deployment
|
||||
* targets.
|
||||
*
|
||||
* hpa_hugify_style_eager
|
||||
* This style results in jemalloc giving hugepage advice, if needed, to
|
||||
* anonymous memory immediately after it is mapped, so huge pages can be backing
|
||||
* that memory at page-fault time. This is usually more efficient than doing
|
||||
* it later, and it allows us to benefit from the hugepages from the start.
|
||||
* Same options for purging as for the style 'none' are good starting choices:
|
||||
* no purging, or purge_threshold=HUGEPAGE, some min_purge_delay_ms that allows
|
||||
* for page not to be purged quickly, etc. This is a good choice if you can
|
||||
* afford extra memory and your application gets performance increase from
|
||||
* transparent hughepages.
|
||||
*
|
||||
* hpa_hugify_style_lazy
|
||||
* This style is suitable when you purge more aggressively (you sacrifice CPU
|
||||
* performance for less memory). When this style is chosen, jemalloc will
|
||||
* hugify once hugification_threshold is reached, and dehugify before purging.
|
||||
* If the kernel is configured to use direct compaction you may experience some
|
||||
* allocation latency when using this style. The best is to measure what works
|
||||
* better for your application needs, and in the target deployment environment.
|
||||
* This is a good choice for apps that cannot afford a lot of memory regression,
|
||||
* but would still like to benefit from backing certain memory regions with
|
||||
* hugepages.
|
||||
*/
|
||||
enum hpa_hugify_style_e {
|
||||
hpa_hugify_style_auto = 0,
|
||||
hpa_hugify_style_none = 1,
|
||||
hpa_hugify_style_eager = 2,
|
||||
hpa_hugify_style_lazy = 3,
|
||||
hpa_hugify_style_limit = hpa_hugify_style_lazy + 1
|
||||
};
|
||||
typedef enum hpa_hugify_style_e hpa_hugify_style_t;
|
||||
|
||||
extern const char *const hpa_hugify_style_names[];
|
||||
|
||||
typedef struct hpa_shard_opts_s hpa_shard_opts_t;
|
||||
struct hpa_shard_opts_s {
|
||||
/*
|
||||
* The largest size we'll allocate out of the shard. For those
|
||||
* allocations refused, the caller (in practice, the PA module) will
|
||||
* fall back to the more general (for now) PAC, which can always handle
|
||||
* any allocation request.
|
||||
*/
|
||||
size_t slab_max_alloc;
|
||||
|
||||
/*
|
||||
* When the number of active bytes in a hugepage is >=
|
||||
* hugification_threshold, we force hugify it.
|
||||
*/
|
||||
size_t hugification_threshold;
|
||||
|
||||
/*
|
||||
* The HPA purges whenever the number of pages exceeds dirty_mult *
|
||||
* active_pages. This may be set to (fxp_t)-1 to disable purging.
|
||||
*/
|
||||
fxp_t dirty_mult;
|
||||
|
||||
/*
|
||||
* Whether or not the PAI methods are allowed to defer work to a
|
||||
* subsequent hpa_shard_do_deferred_work() call. Practically, this
|
||||
* corresponds to background threads being enabled. We track this
|
||||
* ourselves for encapsulation purposes.
|
||||
*/
|
||||
bool deferral_allowed;
|
||||
|
||||
/*
|
||||
* How long a hugepage has to be a hugification candidate before it will
|
||||
* actually get hugified.
|
||||
*/
|
||||
uint64_t hugify_delay_ms;
|
||||
|
||||
/*
|
||||
* Hugify pages synchronously (hugify will happen even if hugify_style
|
||||
* is not hpa_hugify_style_lazy).
|
||||
*/
|
||||
bool hugify_sync;
|
||||
|
||||
/*
|
||||
* Minimum amount of time between purges.
|
||||
*/
|
||||
uint64_t min_purge_interval_ms;
|
||||
|
||||
/*
|
||||
* Maximum number of hugepages to purge on each purging attempt.
|
||||
*/
|
||||
ssize_t experimental_max_purge_nhp;
|
||||
|
||||
/*
|
||||
* Minimum number of inactive bytes needed for a non-empty page to be
|
||||
* considered purgable.
|
||||
*
|
||||
* When the number of touched inactive bytes on non-empty hugepage is
|
||||
* >= purge_threshold, the page is purgable. Empty pages are always
|
||||
* purgable. Setting this to HUGEPAGE bytes would only purge empty
|
||||
* pages if using hugify_style_eager and the purges would be exactly
|
||||
* HUGEPAGE bytes. Depending on your kernel settings, this may result
|
||||
* in better performance.
|
||||
*
|
||||
* Please note, when threshold is reached, we will purge all the dirty
|
||||
* bytes, and not just up to the threshold. If this is PAGE bytes, then
|
||||
* all the pages that have any dirty bytes are purgable. We treat
|
||||
* purgability constraint for purge_threshold as stronger than
|
||||
* dirty_mult, IOW, if no page meets purge_threshold, we will not purge
|
||||
* even if we are above dirty_mult.
|
||||
*/
|
||||
size_t purge_threshold;
|
||||
|
||||
/*
|
||||
* Minimum number of ms that needs to elapse between HP page becoming
|
||||
* eligible for purging and actually getting purged.
|
||||
*
|
||||
* Setting this to a larger number would give better chance of reusing
|
||||
* that memory. Setting it to 0 means that page is eligible for purging
|
||||
* as soon as it meets the purge_threshold. The clock resets when
|
||||
* purgability of the page changes (page goes from being non-purgable to
|
||||
* purgable). When using eager style you probably want to allow for
|
||||
* some delay, to avoid purging the page too quickly and give it time to
|
||||
* be used.
|
||||
*/
|
||||
uint64_t min_purge_delay_ms;
|
||||
|
||||
/*
|
||||
* Style of hugification/dehugification (see comment at
|
||||
* hpa_hugify_style_t for options).
|
||||
*/
|
||||
hpa_hugify_style_t hugify_style;
|
||||
};
|
||||
|
||||
/* clang-format off */
|
||||
#define HPA_SHARD_OPTS_DEFAULT { \
|
||||
/* slab_max_alloc */ \
|
||||
64 * 1024, \
|
||||
/* hugification_threshold */ \
|
||||
HUGEPAGE * 95 / 100, \
|
||||
/* dirty_mult */ \
|
||||
FXP_INIT_PERCENT(25), \
|
||||
/* \
|
||||
* deferral_allowed \
|
||||
* \
|
||||
* Really, this is always set by the arena during creation \
|
||||
* or by an hpa_shard_set_deferral_allowed call, so the value \
|
||||
* we put here doesn't matter. \
|
||||
*/ \
|
||||
false, \
|
||||
/* hugify_delay_ms */ \
|
||||
10 * 1000, \
|
||||
/* hugify_sync */ \
|
||||
false, \
|
||||
/* min_purge_interval_ms */ \
|
||||
5 * 1000, \
|
||||
/* experimental_max_purge_nhp */ \
|
||||
-1, \
|
||||
/* size_t purge_threshold */ \
|
||||
PAGE, \
|
||||
/* min_purge_delay_ms */ \
|
||||
0, \
|
||||
/* hugify_style */ \
|
||||
hpa_hugify_style_lazy \
|
||||
}
|
||||
/* clang-format on */
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HPA_OPTS_H */
|
||||
161
include/jemalloc/internal/hpa_utils.h
Normal file
161
include/jemalloc/internal/hpa_utils.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HPA_UTILS_H
|
||||
#define JEMALLOC_INTERNAL_HPA_UTILS_H
|
||||
|
||||
#include "jemalloc/internal/hpa.h"
|
||||
#include "jemalloc/internal/extent.h"
|
||||
|
||||
#define HPA_MIN_VAR_VEC_SIZE 8
|
||||
/*
|
||||
* This is used for jemalloc internal tuning and may change in the future based
|
||||
* on production traffic.
|
||||
*
|
||||
* This value protects two things:
|
||||
* 1. Stack size
|
||||
* 2. Number of huge pages that are being purged in a batch as we do not
|
||||
* allow allocations while making madvise syscall.
|
||||
*/
|
||||
#define HPA_PURGE_BATCH_MAX 16
|
||||
|
||||
#ifdef JEMALLOC_HAVE_PROCESS_MADVISE
|
||||
typedef struct iovec hpa_io_vector_t;
|
||||
#else
|
||||
typedef struct {
|
||||
void *iov_base;
|
||||
size_t iov_len;
|
||||
} hpa_io_vector_t;
|
||||
#endif
|
||||
|
||||
static inline size_t
|
||||
hpa_process_madvise_max_iovec_len(void) {
|
||||
assert(
|
||||
opt_process_madvise_max_batch <= PROCESS_MADVISE_MAX_BATCH_LIMIT);
|
||||
return opt_process_madvise_max_batch == 0
|
||||
? HPA_MIN_VAR_VEC_SIZE
|
||||
: opt_process_madvise_max_batch;
|
||||
}
|
||||
|
||||
/* Actually invoke hooks. If we fail vectorized, use single purges */
|
||||
static void
|
||||
hpa_try_vectorized_purge(
|
||||
hpa_hooks_t *hooks, hpa_io_vector_t *vec, size_t vlen, size_t nbytes) {
|
||||
bool success = opt_process_madvise_max_batch > 0
|
||||
&& !hooks->vectorized_purge(vec, vlen, nbytes);
|
||||
if (!success) {
|
||||
/* On failure, it is safe to purge again (potential perf
|
||||
* penalty) If kernel can tell exactly which regions
|
||||
* failed, we could avoid that penalty.
|
||||
*/
|
||||
for (size_t i = 0; i < vlen; ++i) {
|
||||
hooks->purge(vec[i].iov_base, vec[i].iov_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This structure accumulates the regions for process_madvise. It invokes the
|
||||
* hook when batch limit is reached.
|
||||
*/
|
||||
typedef struct {
|
||||
hpa_io_vector_t *vp;
|
||||
size_t cur;
|
||||
size_t total_bytes;
|
||||
size_t capacity;
|
||||
} hpa_range_accum_t;
|
||||
|
||||
static inline void
|
||||
hpa_range_accum_init(hpa_range_accum_t *ra, hpa_io_vector_t *v, size_t sz) {
|
||||
ra->vp = v;
|
||||
ra->capacity = sz;
|
||||
ra->total_bytes = 0;
|
||||
ra->cur = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpa_range_accum_flush(hpa_range_accum_t *ra, hpa_hooks_t *hooks) {
|
||||
assert(ra->total_bytes > 0 && ra->cur > 0);
|
||||
hpa_try_vectorized_purge(hooks, ra->vp, ra->cur, ra->total_bytes);
|
||||
ra->cur = 0;
|
||||
ra->total_bytes = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpa_range_accum_add(
|
||||
hpa_range_accum_t *ra, void *addr, size_t sz, hpa_hooks_t *hooks) {
|
||||
assert(ra->cur < ra->capacity);
|
||||
|
||||
ra->vp[ra->cur].iov_base = addr;
|
||||
ra->vp[ra->cur].iov_len = sz;
|
||||
ra->total_bytes += sz;
|
||||
ra->cur++;
|
||||
|
||||
if (ra->cur == ra->capacity) {
|
||||
hpa_range_accum_flush(ra, hooks);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpa_range_accum_finish(hpa_range_accum_t *ra, hpa_hooks_t *hooks) {
|
||||
if (ra->cur > 0) {
|
||||
hpa_range_accum_flush(ra, hooks);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For purging more than one page we use batch of these items
|
||||
*/
|
||||
typedef struct {
|
||||
hpdata_purge_state_t state;
|
||||
hpdata_t *hp;
|
||||
bool dehugify;
|
||||
} hpa_purge_item_t;
|
||||
|
||||
typedef struct hpa_purge_batch_s hpa_purge_batch_t;
|
||||
struct hpa_purge_batch_s {
|
||||
hpa_purge_item_t *items;
|
||||
size_t items_capacity;
|
||||
/* Number of huge pages to purge in current batch */
|
||||
size_t item_cnt;
|
||||
/* Number of ranges to purge in current batch */
|
||||
size_t nranges;
|
||||
/* Total number of dirty pages in current batch*/
|
||||
size_t ndirty_in_batch;
|
||||
|
||||
/* Max number of huge pages to purge */
|
||||
size_t max_hp;
|
||||
/*
|
||||
* Once we are above this watermark we should not add more pages
|
||||
* to the same batch. This is because while we want to minimize
|
||||
* number of madvise calls we also do not want to be preventing
|
||||
* allocations from too many huge pages (which we have to do
|
||||
* while they are being purged)
|
||||
*/
|
||||
size_t range_watermark;
|
||||
|
||||
size_t npurged_hp_total;
|
||||
};
|
||||
|
||||
static inline bool
|
||||
hpa_batch_full(hpa_purge_batch_t *b) {
|
||||
/* It's okay for ranges to go above */
|
||||
return b->npurged_hp_total == b->max_hp
|
||||
|| b->item_cnt == b->items_capacity
|
||||
|| b->nranges >= b->range_watermark;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpa_batch_pass_start(hpa_purge_batch_t *b) {
|
||||
b->item_cnt = 0;
|
||||
b->nranges = 0;
|
||||
b->ndirty_in_batch = 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpa_batch_empty(hpa_purge_batch_t *b) {
|
||||
return b->item_cnt == 0;
|
||||
}
|
||||
|
||||
/* Purge pages in a batch using given hooks */
|
||||
void hpa_purge_batch(
|
||||
hpa_hooks_t *hooks, hpa_purge_item_t *batch, size_t batch_sz);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HPA_UTILS_H */
|
||||
486
include/jemalloc/internal/hpdata.h
Normal file
486
include/jemalloc/internal/hpdata.h
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
#ifndef JEMALLOC_INTERNAL_HPDATA_H
|
||||
#define JEMALLOC_INTERNAL_HPDATA_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/fb.h"
|
||||
#include "jemalloc/internal/nstime.h"
|
||||
#include "jemalloc/internal/pages.h"
|
||||
#include "jemalloc/internal/ph.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/typed_list.h"
|
||||
|
||||
/*
|
||||
* The metadata representation we use for extents in hugepages. While the PAC
|
||||
* uses the edata_t to represent both active and inactive extents, the HP only
|
||||
* uses the edata_t for active ones; instead, inactive extent state is tracked
|
||||
* within hpdata associated with the enclosing hugepage-sized, hugepage-aligned
|
||||
* region of virtual address space.
|
||||
*
|
||||
* An hpdata need not be "truly" backed by a hugepage (which is not necessarily
|
||||
* an observable property of any given region of address space). It's just
|
||||
* hugepage-sized and hugepage-aligned; it's *potentially* huge.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The max enumeration num should not exceed 2^16 - 1, see comments in edata.h
|
||||
* for ESET_ENUMERATE_MAX_NUM for more details.
|
||||
*/
|
||||
#define PSSET_ENUMERATE_MAX_NUM 32
|
||||
typedef struct hpdata_s hpdata_t;
|
||||
ph_structs(hpdata_age_heap, hpdata_t, PSSET_ENUMERATE_MAX_NUM);
|
||||
struct hpdata_s {
|
||||
/*
|
||||
* We likewise follow the edata convention of mangling names and forcing
|
||||
* the use of accessors -- this lets us add some consistency checks on
|
||||
* access.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The address of the hugepage in question. This can't be named h_addr,
|
||||
* since that conflicts with a macro defined in Windows headers.
|
||||
*/
|
||||
void *h_address;
|
||||
/* Its age (measured in psset operations). */
|
||||
uint64_t h_age;
|
||||
/* Whether or not we think the hugepage is mapped that way by the OS. */
|
||||
bool h_huge;
|
||||
|
||||
/*
|
||||
* For some properties, we keep parallel sets of bools; h_foo_allowed
|
||||
* and h_in_psset_foo_container. This is a decoupling mechanism to
|
||||
* avoid bothering the hpa (which manages policies) from the psset
|
||||
* (which is the mechanism used to enforce those policies). This allows
|
||||
* all the container management logic to live in one place, without the
|
||||
* HPA needing to know or care how that happens.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Whether or not the hpdata is allowed to be used to serve allocations,
|
||||
* and whether or not the psset is currently tracking it as such.
|
||||
*/
|
||||
bool h_alloc_allowed;
|
||||
bool h_in_psset_alloc_container;
|
||||
|
||||
/*
|
||||
* The same, but with purging. There's no corresponding
|
||||
* h_in_psset_purge_container, because the psset (currently) always
|
||||
* removes hpdatas from their containers during updates (to implement
|
||||
* LRU for purging).
|
||||
*/
|
||||
bool h_purge_allowed;
|
||||
|
||||
/* And with hugifying. */
|
||||
bool h_hugify_allowed;
|
||||
/* When we became a hugification candidate. */
|
||||
nstime_t h_time_hugify_allowed;
|
||||
bool h_in_psset_hugify_container;
|
||||
|
||||
/* Whether or not a purge or hugify is currently happening. */
|
||||
bool h_mid_purge;
|
||||
bool h_mid_hugify;
|
||||
|
||||
/*
|
||||
* Whether or not the hpdata is being updated in the psset (i.e. if
|
||||
* there has been a psset_update_begin call issued without a matching
|
||||
* psset_update_end call). Eventually this will expand to other types
|
||||
* of updates.
|
||||
*/
|
||||
bool h_updating;
|
||||
|
||||
/* Whether or not the hpdata is in a psset. */
|
||||
bool h_in_psset;
|
||||
|
||||
union {
|
||||
/* When nonempty (and also nonfull), used by the psset bins. */
|
||||
hpdata_age_heap_link_t age_link;
|
||||
/*
|
||||
* When empty (or not corresponding to any hugepage), list
|
||||
* linkage.
|
||||
*/
|
||||
ql_elm(hpdata_t) ql_link_empty;
|
||||
};
|
||||
|
||||
/*
|
||||
* Linkage for the psset to track candidates for purging and hugifying.
|
||||
*/
|
||||
ql_elm(hpdata_t) ql_link_purge;
|
||||
ql_elm(hpdata_t) ql_link_hugify;
|
||||
|
||||
/* The length of the largest contiguous sequence of inactive pages. */
|
||||
size_t h_longest_free_range;
|
||||
|
||||
/* Number of active pages. */
|
||||
size_t h_nactive;
|
||||
|
||||
/* A bitmap with bits set in the active pages. */
|
||||
fb_group_t active_pages[FB_NGROUPS(HUGEPAGE_PAGES)];
|
||||
|
||||
/*
|
||||
* Number of dirty or active pages, and a bitmap tracking them. One
|
||||
* way to think of this is as which pages are dirty from the OS's
|
||||
* perspective.
|
||||
*/
|
||||
size_t h_ntouched;
|
||||
|
||||
/* The touched pages (using the same definition as above). */
|
||||
fb_group_t touched_pages[FB_NGROUPS(HUGEPAGE_PAGES)];
|
||||
|
||||
/* Time when this extent (hpdata) becomes eligible for purging */
|
||||
nstime_t h_time_purge_allowed;
|
||||
|
||||
/* True if the extent was huge and empty last time when it was purged */
|
||||
bool h_purged_when_empty_and_huge;
|
||||
};
|
||||
|
||||
TYPED_LIST(hpdata_empty_list, hpdata_t, ql_link_empty)
|
||||
TYPED_LIST(hpdata_purge_list, hpdata_t, ql_link_purge)
|
||||
TYPED_LIST(hpdata_hugify_list, hpdata_t, ql_link_hugify)
|
||||
|
||||
ph_proto(, hpdata_age_heap, hpdata_t);
|
||||
|
||||
static inline void *
|
||||
hpdata_addr_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_address;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_addr_set(hpdata_t *hpdata, void *addr) {
|
||||
assert(HUGEPAGE_ADDR2BASE(addr) == addr);
|
||||
hpdata->h_address = addr;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
hpdata_age_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_age;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_age_set(hpdata_t *hpdata, uint64_t age) {
|
||||
hpdata->h_age = age;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_huge_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_huge;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_alloc_allowed_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_alloc_allowed;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_alloc_allowed_set(hpdata_t *hpdata, bool alloc_allowed) {
|
||||
hpdata->h_alloc_allowed = alloc_allowed;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_in_psset_alloc_container_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_in_psset_alloc_container;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_in_psset_alloc_container_set(hpdata_t *hpdata, bool in_container) {
|
||||
assert(in_container != hpdata->h_in_psset_alloc_container);
|
||||
hpdata->h_in_psset_alloc_container = in_container;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_purge_allowed_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_purge_allowed;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_purge_allowed_set(hpdata_t *hpdata, bool purge_allowed) {
|
||||
assert(purge_allowed == false || !hpdata->h_mid_purge);
|
||||
hpdata->h_purge_allowed = purge_allowed;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_hugify_allowed_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_hugify_allowed;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_allow_hugify(hpdata_t *hpdata, nstime_t now) {
|
||||
assert(!hpdata->h_mid_hugify);
|
||||
hpdata->h_hugify_allowed = true;
|
||||
hpdata->h_time_hugify_allowed = now;
|
||||
}
|
||||
|
||||
static inline nstime_t
|
||||
hpdata_time_hugify_allowed(hpdata_t *hpdata) {
|
||||
return hpdata->h_time_hugify_allowed;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_disallow_hugify(hpdata_t *hpdata) {
|
||||
hpdata->h_hugify_allowed = false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_in_psset_hugify_container_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_in_psset_hugify_container;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_in_psset_hugify_container_set(hpdata_t *hpdata, bool in_container) {
|
||||
assert(in_container != hpdata->h_in_psset_hugify_container);
|
||||
hpdata->h_in_psset_hugify_container = in_container;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_mid_purge_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_mid_purge;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_mid_purge_set(hpdata_t *hpdata, bool mid_purge) {
|
||||
assert(mid_purge != hpdata->h_mid_purge);
|
||||
hpdata->h_mid_purge = mid_purge;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_mid_hugify_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_mid_hugify;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_mid_hugify_set(hpdata_t *hpdata, bool mid_hugify) {
|
||||
assert(mid_hugify != hpdata->h_mid_hugify);
|
||||
hpdata->h_mid_hugify = mid_hugify;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_changing_state_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_mid_purge || hpdata->h_mid_hugify;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_updating_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_updating;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_updating_set(hpdata_t *hpdata, bool updating) {
|
||||
assert(updating != hpdata->h_updating);
|
||||
hpdata->h_updating = updating;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_in_psset_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_in_psset;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_in_psset_set(hpdata_t *hpdata, bool in_psset) {
|
||||
assert(in_psset != hpdata->h_in_psset);
|
||||
hpdata->h_in_psset = in_psset;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
hpdata_longest_free_range_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_longest_free_range;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_longest_free_range_set(hpdata_t *hpdata, size_t longest_free_range) {
|
||||
assert(longest_free_range <= HUGEPAGE_PAGES);
|
||||
hpdata->h_longest_free_range = longest_free_range;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
hpdata_nactive_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_nactive;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
hpdata_ntouched_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_ntouched;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
hpdata_ndirty_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_ntouched - hpdata->h_nactive;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
hpdata_nretained_get(hpdata_t *hpdata) {
|
||||
return HUGEPAGE_PAGES - hpdata->h_ntouched;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_time_purge_allowed_set(hpdata_t *hpdata, const nstime_t *v) {
|
||||
nstime_copy(&hpdata->h_time_purge_allowed, v);
|
||||
}
|
||||
|
||||
static inline const nstime_t *
|
||||
hpdata_time_purge_allowed_get(const hpdata_t *hpdata) {
|
||||
return &hpdata->h_time_purge_allowed;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_purged_when_empty_and_huge_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_purged_when_empty_and_huge;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_purged_when_empty_and_huge_set(hpdata_t *hpdata, bool v) {
|
||||
hpdata->h_purged_when_empty_and_huge = v;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_assert_empty(hpdata_t *hpdata) {
|
||||
assert(fb_empty(hpdata->active_pages, HUGEPAGE_PAGES));
|
||||
assert(hpdata->h_nactive == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only used in tests, and in hpdata_assert_consistent, below. Verifies some
|
||||
* consistency properties of the hpdata (e.g. that cached counts of page stats
|
||||
* match computed ones).
|
||||
*/
|
||||
static inline bool
|
||||
hpdata_consistent(hpdata_t *hpdata) {
|
||||
bool res = true;
|
||||
|
||||
const size_t active_urange_longest = fb_urange_longest(
|
||||
hpdata->active_pages, HUGEPAGE_PAGES);
|
||||
const size_t longest_free_range = hpdata_longest_free_range_get(hpdata);
|
||||
if (active_urange_longest != longest_free_range) {
|
||||
malloc_printf(
|
||||
"<jemalloc>: active_fb_urange_longest=%zu != hpdata_longest_free_range=%zu\n",
|
||||
active_urange_longest, longest_free_range);
|
||||
res = false;
|
||||
}
|
||||
|
||||
const size_t active_scount = fb_scount(
|
||||
hpdata->active_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES);
|
||||
if (active_scount != hpdata->h_nactive) {
|
||||
malloc_printf(
|
||||
"<jemalloc>: active_fb_scount=%zu != hpdata_nactive=%zu\n",
|
||||
active_scount, hpdata->h_nactive);
|
||||
res = false;
|
||||
}
|
||||
|
||||
const size_t touched_scount = fb_scount(
|
||||
hpdata->touched_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES);
|
||||
if (touched_scount != hpdata->h_ntouched) {
|
||||
malloc_printf(
|
||||
"<jemalloc>: touched_fb_scount=%zu != hpdata_ntouched=%zu\n",
|
||||
touched_scount, hpdata->h_ntouched);
|
||||
res = false;
|
||||
}
|
||||
|
||||
if (hpdata->h_ntouched < hpdata->h_nactive) {
|
||||
malloc_printf(
|
||||
"<jemalloc>: hpdata_ntouched=%zu < hpdata_nactive=%zu\n",
|
||||
hpdata->h_ntouched, hpdata->h_nactive);
|
||||
res = false;
|
||||
}
|
||||
|
||||
if (hpdata->h_huge && (hpdata->h_ntouched != HUGEPAGE_PAGES)) {
|
||||
malloc_printf(
|
||||
"<jemalloc>: hpdata_huge=%d && (hpdata_ntouched=%zu != hugepage_pages=%zu)\n",
|
||||
hpdata->h_huge, hpdata->h_ntouched, HUGEPAGE_PAGES);
|
||||
res = false;
|
||||
}
|
||||
|
||||
const bool changing_state = hpdata_changing_state_get(hpdata);
|
||||
if (changing_state
|
||||
&& (hpdata->h_purge_allowed || hpdata->h_hugify_allowed)) {
|
||||
malloc_printf(
|
||||
"<jemalloc>: hpdata_changing_state=%d && (hpdata_purge_allowed=%d || hpdata_hugify_allowed=%d)\n",
|
||||
changing_state, hpdata->h_purge_allowed,
|
||||
hpdata->h_hugify_allowed);
|
||||
res = false;
|
||||
}
|
||||
|
||||
if (hpdata_hugify_allowed_get(hpdata)
|
||||
!= hpdata_in_psset_hugify_container_get(hpdata)) {
|
||||
malloc_printf(
|
||||
"<jemalloc>: hpdata_hugify_allowed=%d != hpdata_in_psset_hugify_container=%d\n",
|
||||
hpdata_hugify_allowed_get(hpdata),
|
||||
hpdata_in_psset_hugify_container_get(hpdata));
|
||||
res = false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define hpdata_assert_consistent(hpdata) \
|
||||
do { \
|
||||
assert(hpdata_consistent(hpdata)); \
|
||||
} while (0)
|
||||
|
||||
static inline bool
|
||||
hpdata_empty(const hpdata_t *hpdata) {
|
||||
return hpdata->h_nactive == 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
hpdata_full(const hpdata_t *hpdata) {
|
||||
return hpdata->h_nactive == HUGEPAGE_PAGES;
|
||||
}
|
||||
|
||||
void hpdata_init(hpdata_t *hpdata, void *addr, uint64_t age, bool is_huge);
|
||||
|
||||
/*
|
||||
* Given an hpdata which can serve an allocation request, pick and reserve an
|
||||
* offset within that allocation.
|
||||
*/
|
||||
void *hpdata_reserve_alloc(hpdata_t *hpdata, size_t sz);
|
||||
void hpdata_unreserve(hpdata_t *hpdata, void *addr, size_t sz);
|
||||
|
||||
/*
|
||||
* The hpdata_purge_prepare_t allows grabbing the metadata required to purge
|
||||
* subranges of a hugepage while holding a lock, drop the lock during the actual
|
||||
* purging of them, and reacquire it to update the metadata again.
|
||||
*/
|
||||
typedef struct hpdata_purge_state_s hpdata_purge_state_t;
|
||||
struct hpdata_purge_state_s {
|
||||
size_t npurged;
|
||||
size_t ndirty_to_purge;
|
||||
fb_group_t to_purge[FB_NGROUPS(HUGEPAGE_PAGES)];
|
||||
size_t next_purge_search_begin;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes purge state. The access to hpdata must be externally
|
||||
* synchronized with other hpdata_* calls.
|
||||
*
|
||||
* You can tell whether or not a thread is purging or hugifying a given hpdata
|
||||
* via hpdata_changing_state_get(hpdata). Racing hugification or purging
|
||||
* operations aren't allowed.
|
||||
*
|
||||
* Once you begin purging, you have to follow through and call hpdata_purge_next
|
||||
* until you're done, and then end. Allocating out of an hpdata undergoing
|
||||
* purging is not allowed.
|
||||
*
|
||||
* Returns the number of dirty pages that will be purged and sets nranges
|
||||
* to number of ranges with dirty pages that will be purged.
|
||||
*/
|
||||
size_t hpdata_purge_begin(
|
||||
hpdata_t *hpdata, hpdata_purge_state_t *purge_state, size_t *nranges);
|
||||
|
||||
/*
|
||||
* If there are more extents to purge, sets *r_purge_addr and *r_purge_size to
|
||||
* true, and returns true. Otherwise, returns false to indicate that we're
|
||||
* done.
|
||||
*
|
||||
* This requires exclusive access to the purge state, but *not* to the hpdata.
|
||||
* In particular, unreserve calls are allowed while purging (i.e. you can dalloc
|
||||
* into one part of the hpdata while purging a different part).
|
||||
*/
|
||||
bool hpdata_purge_next(hpdata_t *hpdata, hpdata_purge_state_t *purge_state,
|
||||
void **r_purge_addr, size_t *r_purge_size);
|
||||
/*
|
||||
* Updates the hpdata metadata after all purging is done. Needs external
|
||||
* synchronization.
|
||||
*/
|
||||
void hpdata_purge_end(hpdata_t *hpdata, hpdata_purge_state_t *purge_state);
|
||||
|
||||
void hpdata_hugify(hpdata_t *hpdata);
|
||||
void hpdata_dehugify(hpdata_t *hpdata);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HPDATA_H */
|
||||
43
include/jemalloc/internal/inspect.h
Normal file
43
include/jemalloc/internal/inspect.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef JEMALLOC_INTERNAL_INSPECT_H
|
||||
#define JEMALLOC_INTERNAL_INSPECT_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
/*
|
||||
* This module contains the heap introspection capabilities. For now they are
|
||||
* exposed purely through mallctl APIs in the experimental namespace, but this
|
||||
* may change over time.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following two structs are for experimental purposes. See
|
||||
* experimental_utilization_query_ctl and
|
||||
* experimental_utilization_batch_query_ctl in src/ctl.c.
|
||||
*/
|
||||
typedef struct inspect_extent_util_stats_s inspect_extent_util_stats_t;
|
||||
struct inspect_extent_util_stats_s {
|
||||
size_t nfree;
|
||||
size_t nregs;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef struct inspect_extent_util_stats_verbose_s
|
||||
inspect_extent_util_stats_verbose_t;
|
||||
|
||||
struct inspect_extent_util_stats_verbose_s {
|
||||
void *slabcur_addr;
|
||||
size_t nfree;
|
||||
size_t nregs;
|
||||
size_t size;
|
||||
size_t bin_nfree;
|
||||
size_t bin_nregs;
|
||||
};
|
||||
|
||||
void inspect_extent_util_stats_get(
|
||||
tsdn_t *tsdn, const void *ptr, size_t *nfree, size_t *nregs, size_t *size);
|
||||
void inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
|
||||
size_t *nfree, size_t *nregs, size_t *size, size_t *bin_nfree,
|
||||
size_t *bin_nregs, void **slabcur_addr);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INSPECT_H */
|
||||
|
|
@ -3,56 +3,65 @@
|
|||
|
||||
#include <math.h>
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include "msvc_compat/windows_extra.h"
|
||||
# ifdef _WIN64
|
||||
# if LG_VADDR <= 32
|
||||
# error Generate the headers using x64 vcargs
|
||||
# endif
|
||||
# else
|
||||
# if LG_VADDR > 32
|
||||
# undef LG_VADDR
|
||||
# define LG_VADDR 32
|
||||
# endif
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include "msvc_compat/windows_extra.h"
|
||||
# include "msvc_compat/strings.h"
|
||||
# ifdef _WIN64
|
||||
# if LG_VADDR <= 32
|
||||
# error Generate the headers using x64 vcargs
|
||||
# endif
|
||||
# else
|
||||
# if LG_VADDR > 32
|
||||
# undef LG_VADDR
|
||||
# define LG_VADDR 32
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# include <sys/param.h>
|
||||
# include <sys/mman.h>
|
||||
# if !defined(__pnacl__) && !defined(__native_client__)
|
||||
# include <sys/syscall.h>
|
||||
# if !defined(SYS_write) && defined(__NR_write)
|
||||
# define SYS_write __NR_write
|
||||
# endif
|
||||
# if defined(SYS_open) && defined(__aarch64__)
|
||||
/* Android headers may define SYS_open to __NR_open even though
|
||||
# include <sys/param.h>
|
||||
# include <sys/mman.h>
|
||||
# if !defined(__pnacl__) && !defined(__native_client__)
|
||||
# include <sys/syscall.h>
|
||||
# if !defined(SYS_write) && defined(__NR_write)
|
||||
# define SYS_write __NR_write
|
||||
# endif
|
||||
# if defined(SYS_open) && defined(__aarch64__)
|
||||
/* Android headers may define SYS_open to __NR_open even though
|
||||
* __NR_open may not exist on AArch64 (superseded by __NR_openat). */
|
||||
# undef SYS_open
|
||||
# endif
|
||||
# include <sys/uio.h>
|
||||
# endif
|
||||
# include <pthread.h>
|
||||
# include <signal.h>
|
||||
# ifdef JEMALLOC_OS_UNFAIR_LOCK
|
||||
# include <os/lock.h>
|
||||
# endif
|
||||
# ifdef JEMALLOC_GLIBC_MALLOC_HOOK
|
||||
# include <sched.h>
|
||||
# endif
|
||||
# include <errno.h>
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
# ifdef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
|
||||
# include <mach/mach_time.h>
|
||||
# endif
|
||||
# undef SYS_open
|
||||
# endif
|
||||
# include <sys/uio.h>
|
||||
# endif
|
||||
# include <pthread.h>
|
||||
# if defined(__FreeBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__OpenBSD__)
|
||||
# include <pthread_np.h>
|
||||
# include <sched.h>
|
||||
# if defined(__FreeBSD__)
|
||||
# define cpu_set_t cpuset_t
|
||||
# endif
|
||||
# endif
|
||||
# include <signal.h>
|
||||
# ifdef JEMALLOC_OS_UNFAIR_LOCK
|
||||
# include <os/lock.h>
|
||||
# endif
|
||||
# ifdef JEMALLOC_GLIBC_MALLOC_HOOK
|
||||
# include <sched.h>
|
||||
# endif
|
||||
# include <errno.h>
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
# ifdef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
|
||||
# include <mach/mach_time.h>
|
||||
# endif
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <limits.h>
|
||||
#ifndef SIZE_T_MAX
|
||||
# define SIZE_T_MAX SIZE_MAX
|
||||
# define SIZE_T_MAX SIZE_MAX
|
||||
#endif
|
||||
#ifndef SSIZE_MAX
|
||||
# define SSIZE_MAX ((ssize_t)(SIZE_T_MAX >> 1))
|
||||
# define SSIZE_MAX ((ssize_t)(SIZE_T_MAX >> 1))
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -61,31 +70,57 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#ifndef offsetof
|
||||
# define offsetof(type, member) ((size_t)&(((type *)NULL)->member))
|
||||
# define offsetof(type, member) ((size_t) & (((type *)NULL)->member))
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <ctype.h>
|
||||
#ifdef _MSC_VER
|
||||
# include <io.h>
|
||||
# include <io.h>
|
||||
typedef intptr_t ssize_t;
|
||||
# define PATH_MAX 1024
|
||||
# define STDERR_FILENO 2
|
||||
# define __func__ __FUNCTION__
|
||||
# ifdef JEMALLOC_HAS_RESTRICT
|
||||
# define restrict __restrict
|
||||
# endif
|
||||
# define PATH_MAX 1024
|
||||
# define STDERR_FILENO 2
|
||||
# define __func__ __FUNCTION__
|
||||
# ifdef JEMALLOC_HAS_RESTRICT
|
||||
# define restrict __restrict
|
||||
# endif
|
||||
/* Disable warnings about deprecated system functions. */
|
||||
# pragma warning(disable: 4996)
|
||||
#if _MSC_VER < 1800
|
||||
# pragma warning(disable : 4996)
|
||||
# if _MSC_VER < 1800
|
||||
static int
|
||||
isblank(int c) {
|
||||
return (c == '\t' || c == ' ');
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
/*
|
||||
* The Win32 midl compiler has #define small char; we don't use midl, but
|
||||
* "small" is a nice identifier to have available when talking about size
|
||||
* classes.
|
||||
*/
|
||||
#ifdef small
|
||||
# undef small
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Oftentimes we'd like to perform some kind of arithmetic to obtain
|
||||
* a pointer from another pointer but with some offset or mask applied.
|
||||
* Naively you would accomplish this by casting the source pointer to
|
||||
* `uintptr_t`, performing all of the relevant arithmetic, and then casting
|
||||
* the result to the desired pointer type. However, this has the unfortunate
|
||||
* side-effect of concealing pointer provenance, hiding useful information for
|
||||
* optimization from the compiler (see here for details:
|
||||
* https://clang.llvm.org/extra/clang-tidy/checks/performance/no-int-to-ptr.html
|
||||
* )
|
||||
* Instead what one should do is cast the source pointer to `char *` and perform
|
||||
* the equivalent arithmetic (since `char` of course represents one byte). But
|
||||
* because `char *` has the semantic meaning of "string", we define this typedef
|
||||
* simply to make it clearer where we are performing such pointer arithmetic.
|
||||
*/
|
||||
typedef char byte_t;
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_H */
|
||||
|
|
|
|||
|
|
@ -14,10 +14,13 @@
|
|||
*/
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_CALLOC
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_FREE
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_FREE_SIZED
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_FREE_ALIGNED_SIZED
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_MALLOC
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_MEMALIGN
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_REALLOC
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_VALLOC
|
||||
#undef JEMALLOC_OVERRIDE___LIBC_PVALLOC
|
||||
#undef JEMALLOC_OVERRIDE___POSIX_MEMALIGN
|
||||
|
||||
/*
|
||||
|
|
@ -48,25 +51,13 @@
|
|||
|
||||
/* Defined if GCC __atomic atomics are available. */
|
||||
#undef JEMALLOC_GCC_ATOMIC_ATOMICS
|
||||
/* and the 8-bit variant support. */
|
||||
#undef JEMALLOC_GCC_U8_ATOMIC_ATOMICS
|
||||
|
||||
/* Defined if GCC __sync atomics are available. */
|
||||
#undef JEMALLOC_GCC_SYNC_ATOMICS
|
||||
|
||||
/*
|
||||
* Defined if __sync_add_and_fetch(uint32_t *, uint32_t) and
|
||||
* __sync_sub_and_fetch(uint32_t *, uint32_t) are available, despite
|
||||
* __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 not being defined (which means the
|
||||
* functions are defined in libgcc instead of being inlines).
|
||||
*/
|
||||
#undef JE_FORCE_SYNC_COMPARE_AND_SWAP_4
|
||||
|
||||
/*
|
||||
* Defined if __sync_add_and_fetch(uint64_t *, uint64_t) and
|
||||
* __sync_sub_and_fetch(uint64_t *, uint64_t) are available, despite
|
||||
* __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 not being defined (which means the
|
||||
* functions are defined in libgcc instead of being inlines).
|
||||
*/
|
||||
#undef JE_FORCE_SYNC_COMPARE_AND_SWAP_8
|
||||
/* and the 8-bit variant support. */
|
||||
#undef JEMALLOC_GCC_U8_SYNC_ATOMICS
|
||||
|
||||
/*
|
||||
* Defined if __builtin_clz() and __builtin_clzl() are available.
|
||||
|
|
@ -78,12 +69,6 @@
|
|||
*/
|
||||
#undef JEMALLOC_OS_UNFAIR_LOCK
|
||||
|
||||
/*
|
||||
* Defined if OSSpin*() functions are available, as provided by Darwin, and
|
||||
* documented in the spinlock(3) manual page.
|
||||
*/
|
||||
#undef JEMALLOC_OSSPIN
|
||||
|
||||
/* Defined if syscall(2) is usable. */
|
||||
#undef JEMALLOC_USE_SYSCALL
|
||||
|
||||
|
|
@ -103,6 +88,15 @@
|
|||
/* Defined if pthread_setname_np(3) is available. */
|
||||
#undef JEMALLOC_HAVE_PTHREAD_SETNAME_NP
|
||||
|
||||
/* Defined if pthread_getname_np(3) is available. */
|
||||
#undef JEMALLOC_HAVE_PTHREAD_GETNAME_NP
|
||||
|
||||
/* Defined if pthread_set_name_np(3) is available. */
|
||||
#undef JEMALLOC_HAVE_PTHREAD_SET_NAME_NP
|
||||
|
||||
/* Defined if pthread_get_name_np(3) is available. */
|
||||
#undef JEMALLOC_HAVE_PTHREAD_GET_NAME_NP
|
||||
|
||||
/*
|
||||
* Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.
|
||||
*/
|
||||
|
|
@ -118,6 +112,16 @@
|
|||
*/
|
||||
#undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
|
||||
|
||||
/*
|
||||
* Defined if clock_gettime(CLOCK_REALTIME, ...) is available.
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_CLOCK_REALTIME
|
||||
|
||||
/*
|
||||
* Defined if clock_gettime_nsec_np(CLOCK_UPTIME_RAW) is available.
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_CLOCK_GETTIME_NSEC_NP
|
||||
|
||||
/*
|
||||
* Defined if _malloc_thread_cleanup() exists. At least in the case of
|
||||
* FreeBSD, pthread_key_create() allocates, which if used during malloc
|
||||
|
|
@ -153,6 +157,9 @@
|
|||
/* JEMALLOC_STATS enables statistics calculation. */
|
||||
#undef JEMALLOC_STATS
|
||||
|
||||
/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
|
||||
#undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
|
||||
|
||||
/* JEMALLOC_PROF enables allocation profiling. */
|
||||
#undef JEMALLOC_PROF
|
||||
|
||||
|
|
@ -165,6 +172,15 @@
|
|||
/* Use gcc intrinsics for profile backtracing if defined. */
|
||||
#undef JEMALLOC_PROF_GCC
|
||||
|
||||
/* Use frame pointer for profile backtracing if defined. Linux only. */
|
||||
#undef JEMALLOC_PROF_FRAME_POINTER
|
||||
|
||||
/* JEMALLOC_PAGEID enabled page id */
|
||||
#undef JEMALLOC_PAGEID
|
||||
|
||||
/* JEMALLOC_HAVE_PRCTL checks prctl */
|
||||
#undef JEMALLOC_HAVE_PRCTL
|
||||
|
||||
/*
|
||||
* JEMALLOC_DSS enables use of sbrk(2) to allocate extents from the data storage
|
||||
* segment (DSS).
|
||||
|
|
@ -177,6 +193,9 @@
|
|||
/* Support utrace(2)-based tracing. */
|
||||
#undef JEMALLOC_UTRACE
|
||||
|
||||
/* Support utrace(2)-based tracing (label based signature). */
|
||||
#undef JEMALLOC_UTRACE_LABEL
|
||||
|
||||
/* Support optional abort() on OOM. */
|
||||
#undef JEMALLOC_XMALLOC
|
||||
|
||||
|
|
@ -192,6 +211,9 @@
|
|||
/* One page is 2^LG_PAGE bytes. */
|
||||
#undef LG_PAGE
|
||||
|
||||
/* Maximum number of regions in a slab. */
|
||||
#undef CONFIG_LG_SLAB_MAXREGS
|
||||
|
||||
/*
|
||||
* One huge page is 2^LG_HUGEPAGE bytes. Note that this is defined even if the
|
||||
* system does not explicitly support huge pages; system calls that require
|
||||
|
|
@ -233,6 +255,12 @@
|
|||
#undef JEMALLOC_INTERNAL_FFSL
|
||||
#undef JEMALLOC_INTERNAL_FFS
|
||||
|
||||
/*
|
||||
* popcount*() functions to use for bitmapping.
|
||||
*/
|
||||
#undef JEMALLOC_INTERNAL_POPCOUNTL
|
||||
#undef JEMALLOC_INTERNAL_POPCOUNT
|
||||
|
||||
/*
|
||||
* If defined, explicitly attempt to more uniformly distribute large allocation
|
||||
* pointer alignments across all cache indices.
|
||||
|
|
@ -245,6 +273,18 @@
|
|||
*/
|
||||
#undef JEMALLOC_LOG
|
||||
|
||||
/*
|
||||
* If defined, use readlinkat() (instead of readlink()) to follow
|
||||
* /etc/malloc_conf.
|
||||
*/
|
||||
#undef JEMALLOC_READLINKAT
|
||||
|
||||
/*
|
||||
* If defined, use getenv() (instead of secure_getenv() or
|
||||
* alternatives) to access MALLOC_CONF.
|
||||
*/
|
||||
#undef JEMALLOC_FORCE_GETENV
|
||||
|
||||
/*
|
||||
* Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
|
||||
*/
|
||||
|
|
@ -268,6 +308,13 @@
|
|||
*/
|
||||
#undef JEMALLOC_HAVE_MADVISE_HUGE
|
||||
|
||||
/*
|
||||
* Defined if best-effort synchronous collapse of the native
|
||||
* pages mapped by the memory range into transparent huge pages is supported
|
||||
* via MADV_COLLAPSE arguments to madvise(2).
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_MADVISE_COLLAPSE
|
||||
|
||||
/*
|
||||
* Methods for purging unused pages differ between operating systems.
|
||||
*
|
||||
|
|
@ -293,12 +340,55 @@
|
|||
*/
|
||||
#undef JEMALLOC_MADVISE_DONTDUMP
|
||||
|
||||
/*
|
||||
* Defined if MADV_[NO]CORE is supported as an argument to madvise.
|
||||
*/
|
||||
#undef JEMALLOC_MADVISE_NOCORE
|
||||
|
||||
/* Defined if process_madvise(2) is available. */
|
||||
#undef JEMALLOC_HAVE_PROCESS_MADVISE
|
||||
|
||||
#undef EXPERIMENTAL_SYS_PROCESS_MADVISE_NR
|
||||
|
||||
/* Defined if mprotect(2) is available. */
|
||||
#undef JEMALLOC_HAVE_MPROTECT
|
||||
|
||||
/* Defined if sys/sdt.h is available and sdt tracing enabled */
|
||||
#undef JEMALLOC_EXPERIMENTAL_USDT_STAP
|
||||
|
||||
/*
|
||||
* Defined if sys/sdt.h is unavailable, sdt tracing enabled, and
|
||||
* platform is supported
|
||||
*/
|
||||
#undef JEMALLOC_EXPERIMENTAL_USDT_CUSTOM
|
||||
|
||||
/*
|
||||
* Defined if transparent huge pages (THPs) are supported via the
|
||||
* MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled.
|
||||
*/
|
||||
#undef JEMALLOC_THP
|
||||
|
||||
/* Defined if posix_madvise is available. */
|
||||
#undef JEMALLOC_HAVE_POSIX_MADVISE
|
||||
|
||||
/*
|
||||
* Method for purging unused pages using posix_madvise.
|
||||
*
|
||||
* posix_madvise(..., POSIX_MADV_DONTNEED)
|
||||
*/
|
||||
#undef JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED
|
||||
#undef JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED_ZEROS
|
||||
|
||||
/*
|
||||
* Defined if memcntl page admin call is supported
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_MEMCNTL
|
||||
|
||||
/*
|
||||
* Defined if malloc_size is supported
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_MALLOC_SIZE
|
||||
|
||||
/* Define if operating system has alloca.h header. */
|
||||
#undef JEMALLOC_HAS_ALLOCA_H
|
||||
|
||||
|
|
@ -335,12 +425,18 @@
|
|||
/* Adaptive mutex support in pthreads. */
|
||||
#undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
|
||||
|
||||
/* gettid() support */
|
||||
#undef JEMALLOC_HAVE_GETTID
|
||||
|
||||
/* GNU specific sched_getcpu support */
|
||||
#undef JEMALLOC_HAVE_SCHED_GETCPU
|
||||
|
||||
/* GNU specific sched_setaffinity support */
|
||||
#undef JEMALLOC_HAVE_SCHED_SETAFFINITY
|
||||
|
||||
/* pthread_setaffinity_np support */
|
||||
#undef JEMALLOC_HAVE_PTHREAD_SETAFFINITY_NP
|
||||
|
||||
/*
|
||||
* If defined, all the features necessary for background threads are present.
|
||||
*/
|
||||
|
|
@ -363,4 +459,36 @@
|
|||
*/
|
||||
#undef JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE
|
||||
|
||||
/* Performs additional safety checks when defined. */
|
||||
#undef JEMALLOC_OPT_SAFETY_CHECKS
|
||||
|
||||
/* Is C++ support being built? */
|
||||
#undef JEMALLOC_ENABLE_CXX
|
||||
|
||||
/* Performs additional size checks when defined. */
|
||||
#undef JEMALLOC_OPT_SIZE_CHECKS
|
||||
|
||||
/* Allows sampled junk and stash for checking use-after-free when defined. */
|
||||
#undef JEMALLOC_UAF_DETECTION
|
||||
|
||||
/* Darwin VM_MAKE_TAG support */
|
||||
#undef JEMALLOC_HAVE_VM_MAKE_TAG
|
||||
|
||||
/* If defined, realloc(ptr, 0) defaults to "free" instead of "alloc". */
|
||||
#undef JEMALLOC_ZERO_REALLOC_DEFAULT_FREE
|
||||
|
||||
/* If defined, use volatile asm during benchmarks. */
|
||||
#undef JEMALLOC_HAVE_ASM_VOLATILE
|
||||
|
||||
/*
|
||||
* If defined, support the use of rdtscp to get the time stamp counter
|
||||
* and the processor ID.
|
||||
*/
|
||||
#undef JEMALLOC_HAVE_RDTSCP
|
||||
|
||||
/* If defined, use __int128 for optimization. */
|
||||
#undef JEMALLOC_HAVE_INT128
|
||||
|
||||
#include "jemalloc/internal/jemalloc_internal_overrides.h"
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_DEFS_H_ */
|
||||
|
|
|
|||
|
|
@ -1,23 +1,55 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_EXTERNS_H
|
||||
|
||||
#include "jemalloc/internal/arena_types.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/fxp.h"
|
||||
#include "jemalloc/internal/hpa_opts.h"
|
||||
#include "jemalloc/internal/nstime.h"
|
||||
#include "jemalloc/internal/sec_opts.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
/* TSD checks this to set thread local slow state accordingly. */
|
||||
extern bool malloc_slow;
|
||||
|
||||
/* Run-time options. */
|
||||
extern bool opt_abort;
|
||||
extern bool opt_abort_conf;
|
||||
extern bool opt_abort;
|
||||
extern bool opt_abort_conf;
|
||||
extern bool opt_trust_madvise;
|
||||
extern bool opt_experimental_hpa_start_huge_if_thp_always;
|
||||
extern bool opt_experimental_hpa_enforce_hugify;
|
||||
extern bool opt_confirm_conf;
|
||||
extern bool opt_hpa;
|
||||
extern hpa_shard_opts_t opt_hpa_opts;
|
||||
extern sec_opts_t opt_hpa_sec_opts;
|
||||
|
||||
extern const char *opt_junk;
|
||||
extern bool opt_junk_alloc;
|
||||
extern bool opt_junk_free;
|
||||
extern bool opt_utrace;
|
||||
extern bool opt_xmalloc;
|
||||
extern bool opt_zero;
|
||||
extern unsigned opt_narenas;
|
||||
extern bool opt_junk_alloc;
|
||||
extern bool opt_junk_free;
|
||||
extern void (*JET_MUTABLE junk_free_callback)(void *ptr, size_t size);
|
||||
extern void (*JET_MUTABLE junk_alloc_callback)(void *ptr, size_t size);
|
||||
extern void (*JET_MUTABLE invalid_conf_abort)(void);
|
||||
extern bool opt_utrace;
|
||||
extern bool opt_xmalloc;
|
||||
extern bool opt_experimental_infallible_new;
|
||||
extern bool opt_experimental_tcache_gc;
|
||||
extern bool opt_zero;
|
||||
extern unsigned opt_narenas;
|
||||
extern fxp_t opt_narenas_ratio;
|
||||
extern zero_realloc_action_t opt_zero_realloc_action;
|
||||
extern malloc_init_t malloc_init_state;
|
||||
extern const char *const zero_realloc_mode_names[];
|
||||
extern atomic_zu_t zero_realloc_count;
|
||||
extern bool opt_cache_oblivious;
|
||||
extern unsigned opt_debug_double_free_max_scan;
|
||||
extern size_t opt_calloc_madvise_threshold;
|
||||
extern bool opt_disable_large_size_classes;
|
||||
|
||||
extern const char *opt_malloc_conf_symlink;
|
||||
extern const char *opt_malloc_conf_env_var;
|
||||
|
||||
/* Escape free-fastpath when ptr & mask == 0 (for sanitization purpose). */
|
||||
extern uintptr_t san_cache_bin_nonfast_mask;
|
||||
|
||||
/* Number of CPUs. */
|
||||
extern unsigned ncpus;
|
||||
|
|
@ -25,29 +57,35 @@ extern unsigned ncpus;
|
|||
/* Number of arenas used for automatic multiplexing of threads and arenas. */
|
||||
extern unsigned narenas_auto;
|
||||
|
||||
/* Base index for manual arenas. */
|
||||
extern unsigned manual_arena_base;
|
||||
|
||||
/*
|
||||
* Arenas that are used to service external requests. Not all elements of the
|
||||
* arenas array are necessarily used; arenas are created lazily as needed.
|
||||
*/
|
||||
extern atomic_p_t arenas[];
|
||||
|
||||
void *a0malloc(size_t size);
|
||||
void a0dalloc(void *ptr);
|
||||
void *bootstrap_malloc(size_t size);
|
||||
void *bootstrap_calloc(size_t num, size_t size);
|
||||
void bootstrap_free(void *ptr);
|
||||
void arena_set(unsigned ind, arena_t *arena);
|
||||
extern unsigned huge_arena_ind;
|
||||
|
||||
void *a0malloc(size_t size);
|
||||
void a0dalloc(void *ptr);
|
||||
void *bootstrap_malloc(size_t size);
|
||||
void *bootstrap_calloc(size_t num, size_t size);
|
||||
void bootstrap_free(void *ptr);
|
||||
void arena_set(unsigned ind, arena_t *arena);
|
||||
unsigned narenas_total_get(void);
|
||||
arena_t *arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
|
||||
arena_tdata_t *arena_tdata_get_hard(tsd_t *tsd, unsigned ind);
|
||||
arena_t *arena_init(tsdn_t *tsdn, unsigned ind, const arena_config_t *config);
|
||||
arena_t *arena_choose_hard(tsd_t *tsd, bool internal);
|
||||
void arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind);
|
||||
void iarena_cleanup(tsd_t *tsd);
|
||||
void arena_cleanup(tsd_t *tsd);
|
||||
void arenas_tdata_cleanup(tsd_t *tsd);
|
||||
void jemalloc_prefork(void);
|
||||
void jemalloc_postfork_parent(void);
|
||||
void jemalloc_postfork_child(void);
|
||||
bool malloc_initialized(void);
|
||||
void arena_migrate(tsd_t *tsd, arena_t *oldarena, arena_t *newarena);
|
||||
void iarena_cleanup(tsd_t *tsd);
|
||||
void arena_cleanup(tsd_t *tsd);
|
||||
size_t batch_alloc(void **ptrs, size_t num, size_t size, int flags);
|
||||
void jemalloc_prefork(void);
|
||||
void jemalloc_postfork_parent(void);
|
||||
void jemalloc_postfork_child(void);
|
||||
void sdallocx_default(void *ptr, size_t size, int flags);
|
||||
void free_default(void *ptr);
|
||||
void *malloc_default(size_t size);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTERNS_H */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* structs, externs, and inlines), and included each header file multiple times
|
||||
* in this file, picking out the portion we want on each pass using the
|
||||
* following #defines:
|
||||
* JEMALLOC_H_TYPES : Preprocessor-defined constants and psuedo-opaque data
|
||||
* JEMALLOC_H_TYPES : Preprocessor-defined constants and pseudo-opaque data
|
||||
* types.
|
||||
* JEMALLOC_H_STRUCTS : Data structures.
|
||||
* JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes.
|
||||
|
|
@ -40,8 +40,6 @@
|
|||
/* TYPES */
|
||||
/******************************************************************************/
|
||||
|
||||
#include "jemalloc/internal/extent_types.h"
|
||||
#include "jemalloc/internal/base_types.h"
|
||||
#include "jemalloc/internal/arena_types.h"
|
||||
#include "jemalloc/internal/tcache_types.h"
|
||||
#include "jemalloc/internal/prof_types.h"
|
||||
|
|
@ -50,11 +48,8 @@
|
|||
/* STRUCTS */
|
||||
/******************************************************************************/
|
||||
|
||||
#include "jemalloc/internal/arena_structs_a.h"
|
||||
#include "jemalloc/internal/extent_structs.h"
|
||||
#include "jemalloc/internal/base_structs.h"
|
||||
#include "jemalloc/internal/prof_structs.h"
|
||||
#include "jemalloc/internal/arena_structs_b.h"
|
||||
#include "jemalloc/internal/arena_structs.h"
|
||||
#include "jemalloc/internal/tcache_structs.h"
|
||||
#include "jemalloc/internal/background_thread_structs.h"
|
||||
|
||||
|
|
@ -63,8 +58,6 @@
|
|||
/******************************************************************************/
|
||||
|
||||
#include "jemalloc/internal/jemalloc_internal_externs.h"
|
||||
#include "jemalloc/internal/extent_externs.h"
|
||||
#include "jemalloc/internal/base_externs.h"
|
||||
#include "jemalloc/internal/arena_externs.h"
|
||||
#include "jemalloc/internal/large_externs.h"
|
||||
#include "jemalloc/internal/tcache_externs.h"
|
||||
|
|
@ -76,19 +69,16 @@
|
|||
/******************************************************************************/
|
||||
|
||||
#include "jemalloc/internal/jemalloc_internal_inlines_a.h"
|
||||
#include "jemalloc/internal/base_inlines.h"
|
||||
/*
|
||||
* Include portions of arena code interleaved with tcache code in order to
|
||||
* resolve circular dependencies.
|
||||
*/
|
||||
#include "jemalloc/internal/prof_inlines_a.h"
|
||||
#include "jemalloc/internal/arena_inlines_a.h"
|
||||
#include "jemalloc/internal/extent_inlines.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_inlines_b.h"
|
||||
#include "jemalloc/internal/tcache_inlines.h"
|
||||
#include "jemalloc/internal/arena_inlines_b.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_inlines_c.h"
|
||||
#include "jemalloc/internal/prof_inlines_b.h"
|
||||
#include "jemalloc/internal/prof_inlines.h"
|
||||
#include "jemalloc/internal/background_thread_inlines.h"
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INCLUDES_H */
|
||||
|
|
|
|||
|
|
@ -1,17 +1,32 @@
|
|||
#ifndef JEMALLOC_INTERNAL_INLINES_A_H
|
||||
#define JEMALLOC_INTERNAL_INLINES_A_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_externs.h"
|
||||
#include "jemalloc/internal/arena_types.h"
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/tcache_externs.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE malloc_cpuid_t
|
||||
malloc_getcpu(void) {
|
||||
assert(have_percpu_arena);
|
||||
#if defined(JEMALLOC_HAVE_SCHED_GETCPU)
|
||||
#if defined(_WIN32)
|
||||
return GetCurrentProcessorNumber();
|
||||
#elif defined(JEMALLOC_HAVE_SCHED_GETCPU)
|
||||
return (malloc_cpuid_t)sched_getcpu();
|
||||
#elif defined(JEMALLOC_HAVE_RDTSCP)
|
||||
unsigned int ecx;
|
||||
asm volatile("rdtscp" : "=c"(ecx)::"eax", "edx");
|
||||
return (malloc_cpuid_t)(ecx & 0xfff);
|
||||
#elif defined(__aarch64__) && defined(__APPLE__)
|
||||
/* Other oses most likely use tpidr_el0 instead */
|
||||
uintptr_t c;
|
||||
asm volatile("mrs %x0, tpidrro_el0" : "=r"(c)::"memory");
|
||||
return (malloc_cpuid_t)(c & (1 << 3) - 1);
|
||||
#else
|
||||
not_reached();
|
||||
return -1;
|
||||
|
|
@ -27,8 +42,8 @@ percpu_arena_choose(void) {
|
|||
assert(cpuid >= 0);
|
||||
|
||||
unsigned arena_ind;
|
||||
if ((opt_percpu_arena == percpu_arena) || ((unsigned)cpuid < ncpus /
|
||||
2)) {
|
||||
if ((opt_percpu_arena == percpu_arena)
|
||||
|| ((unsigned)cpuid < ncpus / 2)) {
|
||||
arena_ind = cpuid;
|
||||
} else {
|
||||
assert(opt_percpu_arena == per_phycpu_arena);
|
||||
|
|
@ -54,31 +69,6 @@ percpu_arena_ind_limit(percpu_arena_mode_t mode) {
|
|||
}
|
||||
}
|
||||
|
||||
static inline arena_tdata_t *
|
||||
arena_tdata_get(tsd_t *tsd, unsigned ind, bool refresh_if_missing) {
|
||||
arena_tdata_t *tdata;
|
||||
arena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);
|
||||
|
||||
if (unlikely(arenas_tdata == NULL)) {
|
||||
/* arenas_tdata hasn't been initialized yet. */
|
||||
return arena_tdata_get_hard(tsd, ind);
|
||||
}
|
||||
if (unlikely(ind >= tsd_narenas_tdata_get(tsd))) {
|
||||
/*
|
||||
* ind is invalid, cache is old (too small), or tdata to be
|
||||
* initialized.
|
||||
*/
|
||||
return (refresh_if_missing ? arena_tdata_get_hard(tsd, ind) :
|
||||
NULL);
|
||||
}
|
||||
|
||||
tdata = &arenas_tdata[ind];
|
||||
if (likely(tdata != NULL) || !refresh_if_missing) {
|
||||
return tdata;
|
||||
}
|
||||
return arena_tdata_get_hard(tsd, ind);
|
||||
}
|
||||
|
||||
static inline arena_t *
|
||||
arena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing) {
|
||||
arena_t *ret;
|
||||
|
|
@ -88,36 +78,12 @@ arena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing) {
|
|||
ret = (arena_t *)atomic_load_p(&arenas[ind], ATOMIC_ACQUIRE);
|
||||
if (unlikely(ret == NULL)) {
|
||||
if (init_if_missing) {
|
||||
ret = arena_init(tsdn, ind,
|
||||
(extent_hooks_t *)&extent_hooks_default);
|
||||
ret = arena_init(tsdn, ind, &arena_config_default);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline ticker_t *
|
||||
decay_ticker_get(tsd_t *tsd, unsigned ind) {
|
||||
arena_tdata_t *tdata;
|
||||
|
||||
tdata = arena_tdata_get(tsd, ind, true);
|
||||
if (unlikely(tdata == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return &tdata->decay_ticker;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE cache_bin_t *
|
||||
tcache_small_bin_get(tcache_t *tcache, szind_t binind) {
|
||||
assert(binind < NBINS);
|
||||
return &tcache->bins_small[binind];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE cache_bin_t *
|
||||
tcache_large_bin_get(tcache_t *tcache, szind_t binind) {
|
||||
assert(binind >= NBINS &&binind < nhbins);
|
||||
return &tcache->bins_large[binind - NBINS];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
tcache_available(tsd_t *tsd) {
|
||||
/*
|
||||
|
|
@ -127,9 +93,9 @@ tcache_available(tsd_t *tsd) {
|
|||
*/
|
||||
if (likely(tsd_tcache_enabled_get(tsd))) {
|
||||
/* Associated arena == NULL implies tcache init in progress. */
|
||||
assert(tsd_tcachep_get(tsd)->arena == NULL ||
|
||||
tcache_small_bin_get(tsd_tcachep_get(tsd), 0)->avail !=
|
||||
NULL);
|
||||
if (config_debug && tsd_tcache_slowp_get(tsd)->arena != NULL) {
|
||||
tcache_assert_initialized(tsd_tcachep_get(tsd));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -145,28 +111,25 @@ tcache_get(tsd_t *tsd) {
|
|||
return tsd_tcachep_get(tsd);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tcache_slow_t *
|
||||
tcache_slow_get(tsd_t *tsd) {
|
||||
if (!tcache_available(tsd)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tsd_tcache_slowp_get(tsd);
|
||||
}
|
||||
|
||||
static inline void
|
||||
pre_reentrancy(tsd_t *tsd, arena_t *arena) {
|
||||
/* arena is the current context. Reentry from a0 is not allowed. */
|
||||
assert(arena != arena_get(tsd_tsdn(tsd), 0, false));
|
||||
|
||||
bool fast = tsd_fast(tsd);
|
||||
assert(tsd_reentrancy_level_get(tsd) < INT8_MAX);
|
||||
++*tsd_reentrancy_levelp_get(tsd);
|
||||
if (fast) {
|
||||
/* Prepare slow path for reentrancy. */
|
||||
tsd_slow_update(tsd);
|
||||
assert(tsd->state == tsd_state_nominal_slow);
|
||||
}
|
||||
tsd_pre_reentrancy_raw(tsd);
|
||||
}
|
||||
|
||||
static inline void
|
||||
post_reentrancy(tsd_t *tsd) {
|
||||
int8_t *reentrancy_level = tsd_reentrancy_levelp_get(tsd);
|
||||
assert(*reentrancy_level > 0);
|
||||
if (--*reentrancy_level == 0) {
|
||||
tsd_slow_update(tsd);
|
||||
}
|
||||
tsd_post_reentrancy_raw(tsd);
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INLINES_A_H */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,34 @@
|
|||
#ifndef JEMALLOC_INTERNAL_INLINES_B_H
|
||||
#define JEMALLOC_INTERNAL_INLINES_B_H
|
||||
|
||||
#include "jemalloc/internal/rtree.h"
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_inlines_a.h"
|
||||
#include "jemalloc/internal/extent.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_inlines_a.h"
|
||||
|
||||
static inline void
|
||||
percpu_arena_update(tsd_t *tsd, unsigned cpu) {
|
||||
assert(have_percpu_arena);
|
||||
arena_t *oldarena = tsd_arena_get(tsd);
|
||||
assert(oldarena != NULL);
|
||||
unsigned oldind = arena_ind_get(oldarena);
|
||||
|
||||
if (oldind != cpu) {
|
||||
unsigned newind = cpu;
|
||||
arena_t *newarena = arena_get(tsd_tsdn(tsd), newind, true);
|
||||
assert(newarena != NULL);
|
||||
|
||||
/* Set new arena/tcache associations. */
|
||||
arena_migrate(tsd, oldarena, newarena);
|
||||
tcache_t *tcache = tcache_get(tsd);
|
||||
if (tcache != NULL) {
|
||||
tcache_slow_t *tcache_slow = tsd_tcache_slowp_get(tsd);
|
||||
assert(tcache_slow->arena != NULL);
|
||||
tcache_arena_reassociate(
|
||||
tsd_tsdn(tsd), tcache_slow, tcache, newarena);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Choose an arena based on a per-thread value. */
|
||||
static inline arena_t *
|
||||
|
|
@ -22,18 +49,19 @@ arena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal) {
|
|||
ret = arena_choose_hard(tsd, internal);
|
||||
assert(ret);
|
||||
if (tcache_available(tsd)) {
|
||||
tcache_t *tcache = tcache_get(tsd);
|
||||
if (tcache->arena != NULL) {
|
||||
/* See comments in tcache_data_init().*/
|
||||
assert(tcache->arena ==
|
||||
arena_get(tsd_tsdn(tsd), 0, false));
|
||||
if (tcache->arena != ret) {
|
||||
tcache_slow_t *tcache_slow = tsd_tcache_slowp_get(tsd);
|
||||
tcache_t *tcache = tsd_tcachep_get(tsd);
|
||||
if (tcache_slow->arena != NULL) {
|
||||
/* See comments in tsd_tcache_data_init().*/
|
||||
assert(tcache_slow->arena
|
||||
== arena_get(tsd_tsdn(tsd), 0, false));
|
||||
if (tcache_slow->arena != ret) {
|
||||
tcache_arena_reassociate(tsd_tsdn(tsd),
|
||||
tcache, ret);
|
||||
tcache_slow, tcache, ret);
|
||||
}
|
||||
} else {
|
||||
tcache_arena_associate(tsd_tsdn(tsd), tcache,
|
||||
ret);
|
||||
tcache_arena_associate(
|
||||
tsd_tsdn(tsd), tcache_slow, tcache, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,10 +71,10 @@ arena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal) {
|
|||
* auto percpu arena range, (i.e. thread is assigned to a manually
|
||||
* managed arena), then percpu arena is skipped.
|
||||
*/
|
||||
if (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena) &&
|
||||
!internal && (arena_ind_get(ret) <
|
||||
percpu_arena_ind_limit(opt_percpu_arena)) && (ret->last_thd !=
|
||||
tsd_tsdn(tsd))) {
|
||||
if (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena)
|
||||
&& !internal
|
||||
&& (arena_ind_get(ret) < percpu_arena_ind_limit(opt_percpu_arena))
|
||||
&& (ret->last_thd != tsd_tsdn(tsd))) {
|
||||
unsigned ind = percpu_arena_choose();
|
||||
if (arena_ind_get(ret) != ind) {
|
||||
percpu_arena_update(tsd, ind);
|
||||
|
|
@ -71,16 +99,8 @@ arena_ichoose(tsd_t *tsd, arena_t *arena) {
|
|||
static inline bool
|
||||
arena_is_auto(arena_t *arena) {
|
||||
assert(narenas_auto > 0);
|
||||
return (arena_ind_get(arena) < narenas_auto);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE extent_t *
|
||||
iealloc(tsdn_t *tsdn, const void *ptr) {
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
|
||||
return rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true);
|
||||
return (arena_ind_get(arena) < manual_arena_base);
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INLINES_B_H */
|
||||
|
|
|
|||
|
|
@ -1,10 +1,26 @@
|
|||
#ifndef JEMALLOC_INTERNAL_INLINES_C_H
|
||||
#define JEMALLOC_INTERNAL_INLINES_C_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/arena_externs.h"
|
||||
#include "jemalloc/internal/arena_inlines_b.h"
|
||||
#include "jemalloc/internal/emap.h"
|
||||
#include "jemalloc/internal/hook.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/log.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
#include "jemalloc/internal/thread_event.h"
|
||||
#include "jemalloc/internal/witness.h"
|
||||
|
||||
/*
|
||||
* These correspond to the macros in jemalloc/jemalloc_macros.h. Broadly, we
|
||||
* should have one constant here per magic value there. Note however that the
|
||||
* representations need not be related.
|
||||
*/
|
||||
#define TCACHE_IND_NONE ((unsigned)-1)
|
||||
#define TCACHE_IND_AUTOMATIC ((unsigned)-2)
|
||||
#define ARENA_IND_AUTOMATIC ((unsigned)-1)
|
||||
|
||||
/*
|
||||
* Translating the names of the 'i' functions:
|
||||
* Abbreviations used in the first part of the function name (before
|
||||
|
|
@ -38,25 +54,35 @@ isalloc(tsdn_t *tsdn, const void *ptr) {
|
|||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,
|
||||
bool is_internal, arena_t *arena, bool slow_path) {
|
||||
iallocztm_explicit_slab(tsdn_t *tsdn, size_t size, szind_t ind, bool zero,
|
||||
bool slab, tcache_t *tcache, bool is_internal, arena_t *arena,
|
||||
bool slow_path) {
|
||||
void *ret;
|
||||
|
||||
assert(size != 0);
|
||||
assert(!slab || sz_can_use_slab(size)); /* slab && large is illegal */
|
||||
assert(!is_internal || tcache == NULL);
|
||||
assert(!is_internal || arena == NULL || arena_is_auto(arena));
|
||||
if (!tsdn_null(tsdn) && tsd_reentrancy_level_get(tsdn_tsd(tsdn)) == 0) {
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
witness_assert_depth_to_rank(
|
||||
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE, 0);
|
||||
}
|
||||
|
||||
ret = arena_malloc(tsdn, arena, size, ind, zero, tcache, slow_path);
|
||||
ret = arena_malloc(
|
||||
tsdn, arena, size, ind, zero, slab, tcache, slow_path);
|
||||
if (config_stats && is_internal && likely(ret != NULL)) {
|
||||
arena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,
|
||||
bool is_internal, arena_t *arena, bool slow_path) {
|
||||
bool slab = sz_can_use_slab(size);
|
||||
return iallocztm_explicit_slab(
|
||||
tsdn, size, ind, zero, slab, tcache, is_internal, arena, slow_path);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero, bool slow_path) {
|
||||
return iallocztm(tsd_tsdn(tsd), size, ind, zero, tcache_get(tsd), false,
|
||||
|
|
@ -64,18 +90,19 @@ ialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero, bool slow_path) {
|
|||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
tcache_t *tcache, bool is_internal, arena_t *arena) {
|
||||
ipallocztm_explicit_slab(tsdn_t *tsdn, size_t usize, size_t alignment,
|
||||
bool zero, bool slab, tcache_t *tcache, bool is_internal, arena_t *arena) {
|
||||
void *ret;
|
||||
|
||||
assert(!slab || sz_can_use_slab(usize)); /* slab && large is illegal */
|
||||
assert(usize != 0);
|
||||
assert(usize == sz_sa2u(usize, alignment));
|
||||
assert(!is_internal || tcache == NULL);
|
||||
assert(!is_internal || arena == NULL || arena_is_auto(arena));
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
witness_assert_depth_to_rank(
|
||||
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE, 0);
|
||||
|
||||
ret = arena_palloc(tsdn, arena, usize, alignment, zero, tcache);
|
||||
ret = arena_palloc(tsdn, arena, usize, alignment, zero, slab, tcache);
|
||||
assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);
|
||||
if (config_stats && is_internal && likely(ret != NULL)) {
|
||||
arena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));
|
||||
|
|
@ -83,12 +110,26 @@ ipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
|||
return ret;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
tcache_t *tcache, bool is_internal, arena_t *arena) {
|
||||
return ipallocztm_explicit_slab(tsdn, usize, alignment, zero,
|
||||
sz_can_use_slab(usize), tcache, is_internal, arena);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipalloct(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
tcache_t *tcache, arena_t *arena) {
|
||||
return ipallocztm(tsdn, usize, alignment, zero, tcache, false, arena);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipalloct_explicit_slab(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
|
||||
bool slab, tcache_t *tcache, arena_t *arena) {
|
||||
return ipallocztm_explicit_slab(
|
||||
tsdn, usize, alignment, zero, slab, tcache, false, arena);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero) {
|
||||
return ipallocztm(tsd_tsdn(tsd), usize, alignment, zero,
|
||||
|
|
@ -101,18 +142,18 @@ ivsalloc(tsdn_t *tsdn, const void *ptr) {
|
|||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache, alloc_ctx_t *alloc_ctx,
|
||||
bool is_internal, bool slow_path) {
|
||||
idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
emap_alloc_ctx_t *alloc_ctx, bool is_internal, bool slow_path) {
|
||||
assert(ptr != NULL);
|
||||
assert(!is_internal || tcache == NULL);
|
||||
assert(!is_internal || arena_is_auto(iaalloc(tsdn, ptr)));
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
witness_assert_depth_to_rank(
|
||||
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE, 0);
|
||||
if (config_stats && is_internal) {
|
||||
arena_internal_sub(iaalloc(tsdn, ptr), isalloc(tsdn, ptr));
|
||||
}
|
||||
if (!is_internal && !tsdn_null(tsdn) &&
|
||||
tsd_reentrancy_level_get(tsdn_tsd(tsdn)) != 0) {
|
||||
if (!is_internal && !tsdn_null(tsdn)
|
||||
&& tsd_reentrancy_level_get(tsdn_tsd(tsdn)) != 0) {
|
||||
assert(tcache == NULL);
|
||||
}
|
||||
arena_dalloc(tsdn, ptr, tcache, alloc_ctx, slow_path);
|
||||
|
|
@ -125,39 +166,29 @@ idalloc(tsd_t *tsd, void *ptr) {
|
|||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
alloc_ctx_t *alloc_ctx, bool slow_path) {
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
emap_alloc_ctx_t *alloc_ctx, bool slow_path) {
|
||||
witness_assert_depth_to_rank(
|
||||
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE, 0);
|
||||
arena_sdalloc(tsdn, ptr, size, tcache, alloc_ctx, slow_path);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, size_t alignment, bool zero, tcache_t *tcache,
|
||||
arena_t *arena) {
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
void *p;
|
||||
size_t alignment, bool zero, bool slab, tcache_t *tcache, arena_t *arena,
|
||||
hook_ralloc_args_t *hook_args) {
|
||||
witness_assert_depth_to_rank(
|
||||
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE, 0);
|
||||
void *p;
|
||||
size_t usize, copysize;
|
||||
|
||||
usize = sz_sa2u(size + extra, alignment);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
usize = sz_sa2u(size, alignment);
|
||||
if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) {
|
||||
return NULL;
|
||||
}
|
||||
p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
||||
p = ipalloct_explicit_slab(
|
||||
tsdn, usize, alignment, zero, slab, tcache, arena);
|
||||
if (p == NULL) {
|
||||
if (extra == 0) {
|
||||
return NULL;
|
||||
}
|
||||
/* Try again, without extra this time. */
|
||||
usize = sz_sa2u(size, alignment);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
return NULL;
|
||||
}
|
||||
p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
||||
if (p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Copy at most size bytes (not size+extra), since the caller has no
|
||||
|
|
@ -165,54 +196,405 @@ iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
|||
*/
|
||||
copysize = (size < oldsize) ? size : oldsize;
|
||||
memcpy(p, ptr, copysize);
|
||||
hook_invoke_alloc(
|
||||
hook_args->is_realloc ? hook_alloc_realloc : hook_alloc_rallocx, p,
|
||||
(uintptr_t)p, hook_args->args);
|
||||
hook_invoke_dalloc(
|
||||
hook_args->is_realloc ? hook_dalloc_realloc : hook_dalloc_rallocx,
|
||||
ptr, hook_args->args);
|
||||
isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_realloc threads through the knowledge of whether or not this call comes
|
||||
* from je_realloc (as opposed to je_rallocx); this ensures that we pass the
|
||||
* correct entry point into any hooks.
|
||||
* Note that these functions are all force-inlined, so no actual bool gets
|
||||
* passed-around anywhere.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||
bool zero, tcache_t *tcache, arena_t *arena) {
|
||||
iralloct_explicit_slab(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t alignment, bool zero, bool slab, tcache_t *tcache, arena_t *arena,
|
||||
hook_ralloc_args_t *hook_args) {
|
||||
assert(ptr != NULL);
|
||||
assert(size != 0);
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
witness_assert_depth_to_rank(
|
||||
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE, 0);
|
||||
|
||||
if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
|
||||
!= 0) {
|
||||
if (alignment != 0
|
||||
&& ((uintptr_t)ptr & ((uintptr_t)alignment - 1)) != 0) {
|
||||
/*
|
||||
* Existing object alignment is inadequate; allocate new space
|
||||
* and copy.
|
||||
*/
|
||||
return iralloct_realign(tsdn, ptr, oldsize, size, 0, alignment,
|
||||
zero, tcache, arena);
|
||||
return iralloct_realign(tsdn, ptr, oldsize, size, alignment,
|
||||
zero, slab, tcache, arena, hook_args);
|
||||
}
|
||||
|
||||
return arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,
|
||||
tcache);
|
||||
slab, tcache, hook_args);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||
size_t usize, bool zero, tcache_t *tcache, arena_t *arena,
|
||||
hook_ralloc_args_t *hook_args) {
|
||||
bool slab = sz_can_use_slab(usize);
|
||||
return iralloct_explicit_slab(tsdn, ptr, oldsize, size, alignment, zero,
|
||||
slab, tcache, arena, hook_args);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||
bool zero) {
|
||||
return iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,
|
||||
tcache_get(tsd), NULL);
|
||||
size_t usize, bool zero, hook_ralloc_args_t *hook_args) {
|
||||
return iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, usize,
|
||||
zero, tcache_get(tsd), NULL, hook_args);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
size_t alignment, bool zero) {
|
||||
size_t alignment, bool zero, size_t *newsize) {
|
||||
assert(ptr != NULL);
|
||||
assert(size != 0);
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
witness_assert_depth_to_rank(
|
||||
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE, 0);
|
||||
|
||||
if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
|
||||
!= 0) {
|
||||
if (alignment != 0
|
||||
&& ((uintptr_t)ptr & ((uintptr_t)alignment - 1)) != 0) {
|
||||
/* Existing object alignment is inadequate. */
|
||||
*newsize = oldsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
return arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero);
|
||||
return arena_ralloc_no_move(
|
||||
tsdn, ptr, oldsize, size, extra, zero, newsize);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
fastpath_success_finish(
|
||||
tsd_t *tsd, uint64_t allocated_after, cache_bin_t *bin, void *ret) {
|
||||
thread_allocated_set(tsd, allocated_after);
|
||||
if (config_stats) {
|
||||
bin->tstats.nrequests++;
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
malloc_initialized(void) {
|
||||
return (malloc_init_state == malloc_init_initialized);
|
||||
}
|
||||
|
||||
/*
|
||||
* malloc() fastpath. Included here so that we can inline it into operator new;
|
||||
* function call overhead there is non-negligible as a fraction of total CPU in
|
||||
* allocation-heavy C++ programs. We take the fallback alloc to allow malloc
|
||||
* (which can return NULL) to differ in its behavior from operator new (which
|
||||
* can't). It matches the signature of malloc / operator new so that we can
|
||||
* tail-call the fallback allocator, allowing us to avoid setting up the call
|
||||
* frame in the common case.
|
||||
*
|
||||
* Fastpath assumes size <= SC_LOOKUP_MAXCLASS, and that we hit
|
||||
* tcache. If either of these is false, we tail-call to the slowpath,
|
||||
* malloc_default(). Tail-calling is used to avoid any caller-saved
|
||||
* registers.
|
||||
*
|
||||
* fastpath supports ticker and profiling, both of which will also
|
||||
* tail-call to the slowpath if they fire.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
imalloc_fastpath(size_t size, void *(fallback_alloc)(size_t)) {
|
||||
if (tsd_get_allocates() && unlikely(!malloc_initialized())) {
|
||||
return fallback_alloc(size);
|
||||
}
|
||||
|
||||
tsd_t *tsd = tsd_get(false);
|
||||
if (unlikely((size > SC_LOOKUP_MAXCLASS) || tsd == NULL)) {
|
||||
return fallback_alloc(size);
|
||||
}
|
||||
/*
|
||||
* The code below till the branch checking the next_event threshold may
|
||||
* execute before malloc_init(), in which case the threshold is 0 to
|
||||
* trigger slow path and initialization.
|
||||
*
|
||||
* Note that when uninitialized, only the fast-path variants of the sz /
|
||||
* tsd facilities may be called.
|
||||
*/
|
||||
szind_t ind;
|
||||
/*
|
||||
* The thread_allocated counter in tsd serves as a general purpose
|
||||
* accumulator for bytes of allocation to trigger different types of
|
||||
* events. usize is always needed to advance thread_allocated, though
|
||||
* it's not always needed in the core allocation logic.
|
||||
*/
|
||||
size_t usize;
|
||||
sz_size2index_usize_fastpath(size, &ind, &usize);
|
||||
/* Fast path relies on size being a bin. */
|
||||
assert(ind < SC_NBINS);
|
||||
assert((SC_LOOKUP_MAXCLASS < SC_SMALL_MAXCLASS)
|
||||
&& (size <= SC_SMALL_MAXCLASS));
|
||||
|
||||
uint64_t allocated, threshold;
|
||||
te_malloc_fastpath_ctx(tsd, &allocated, &threshold);
|
||||
uint64_t allocated_after = allocated + usize;
|
||||
/*
|
||||
* The ind and usize might be uninitialized (or partially) before
|
||||
* malloc_init(). The assertions check for: 1) full correctness (usize
|
||||
* & ind) when initialized; and 2) guaranteed slow-path (threshold == 0)
|
||||
* when !initialized.
|
||||
*/
|
||||
if (!malloc_initialized()) {
|
||||
assert(threshold == 0);
|
||||
} else {
|
||||
assert(ind == sz_size2index(size));
|
||||
assert(usize > 0 && usize == sz_index2size(ind));
|
||||
}
|
||||
/*
|
||||
* Check for events and tsd non-nominal (fast_threshold will be set to
|
||||
* 0) in a single branch.
|
||||
*/
|
||||
if (unlikely(allocated_after >= threshold)) {
|
||||
return fallback_alloc(size);
|
||||
}
|
||||
assert(tsd_fast(tsd));
|
||||
|
||||
tcache_t *tcache = tsd_tcachep_get(tsd);
|
||||
assert(tcache == tcache_get(tsd));
|
||||
cache_bin_t *bin = &tcache->bins[ind];
|
||||
/* Suppress spurious warning from static analysis */
|
||||
assert(bin != NULL);
|
||||
bool tcache_success;
|
||||
void *ret;
|
||||
|
||||
/*
|
||||
* We split up the code this way so that redundant low-water
|
||||
* computation doesn't happen on the (more common) case in which we
|
||||
* don't touch the low water mark. The compiler won't do this
|
||||
* duplication on its own.
|
||||
*/
|
||||
ret = cache_bin_alloc_easy(bin, &tcache_success);
|
||||
if (tcache_success) {
|
||||
fastpath_success_finish(tsd, allocated_after, bin, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = cache_bin_alloc(bin, &tcache_success);
|
||||
if (tcache_success) {
|
||||
fastpath_success_finish(tsd, allocated_after, bin, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return fallback_alloc(size);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tcache_t *
|
||||
tcache_get_from_ind(tsd_t *tsd, unsigned tcache_ind, bool slow, bool is_alloc) {
|
||||
tcache_t *tcache;
|
||||
if (tcache_ind == TCACHE_IND_AUTOMATIC) {
|
||||
if (likely(!slow)) {
|
||||
/* Getting tcache ptr unconditionally. */
|
||||
tcache = tsd_tcachep_get(tsd);
|
||||
assert(tcache == tcache_get(tsd));
|
||||
} else if (is_alloc
|
||||
|| likely(tsd_reentrancy_level_get(tsd) == 0)) {
|
||||
tcache = tcache_get(tsd);
|
||||
} else {
|
||||
tcache = NULL;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Should not specify tcache on deallocation path when being
|
||||
* reentrant.
|
||||
*/
|
||||
assert(is_alloc || tsd_reentrancy_level_get(tsd) == 0
|
||||
|| tsd_state_nocleanup(tsd));
|
||||
if (tcache_ind == TCACHE_IND_NONE) {
|
||||
tcache = NULL;
|
||||
} else {
|
||||
tcache = tcaches_get(tsd, tcache_ind);
|
||||
}
|
||||
}
|
||||
return tcache;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
maybe_check_alloc_ctx(tsd_t *tsd, void *ptr, emap_alloc_ctx_t *alloc_ctx) {
|
||||
if (config_opt_size_checks) {
|
||||
emap_alloc_ctx_t dbg_ctx;
|
||||
emap_alloc_ctx_lookup(
|
||||
tsd_tsdn(tsd), &arena_emap_global, ptr, &dbg_ctx);
|
||||
if (alloc_ctx->szind != dbg_ctx.szind) {
|
||||
safety_check_fail_sized_dealloc(
|
||||
/* current_dealloc */ true, ptr,
|
||||
/* true_size */ emap_alloc_ctx_usize_get(&dbg_ctx),
|
||||
/* input_size */
|
||||
emap_alloc_ctx_usize_get(alloc_ctx));
|
||||
return true;
|
||||
}
|
||||
if (alloc_ctx->slab != dbg_ctx.slab) {
|
||||
safety_check_fail(
|
||||
"Internal heap corruption detected: "
|
||||
"mismatch in slab bit");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
prof_sample_aligned(const void *ptr) {
|
||||
return ((uintptr_t)ptr & PROF_SAMPLE_ALIGNMENT_MASK) == 0;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
free_fastpath_nonfast_aligned(void *ptr, bool check_prof) {
|
||||
/*
|
||||
* free_fastpath do not handle two uncommon cases: 1) sampled profiled
|
||||
* objects and 2) sampled junk & stash for use-after-free detection.
|
||||
* Both have special alignments which are used to escape the fastpath.
|
||||
*
|
||||
* prof_sample is page-aligned, which covers the UAF check when both
|
||||
* are enabled (the assertion below). Avoiding redundant checks since
|
||||
* this is on the fastpath -- at most one runtime branch from this.
|
||||
*/
|
||||
if (config_debug && cache_bin_nonfast_aligned(ptr)) {
|
||||
assert(prof_sample_aligned(ptr));
|
||||
}
|
||||
|
||||
if (config_prof && check_prof) {
|
||||
/* When prof is enabled, the prof_sample alignment is enough. */
|
||||
if (prof_sample_aligned(ptr)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_uaf_detection) {
|
||||
if (cache_bin_nonfast_aligned(ptr)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns whether or not the free attempt was successful. */
|
||||
JEMALLOC_ALWAYS_INLINE
|
||||
bool
|
||||
free_fastpath(void *ptr, size_t size, bool size_hint) {
|
||||
tsd_t *tsd = tsd_get(false);
|
||||
/* The branch gets optimized away unless tsd_get_allocates(). */
|
||||
if (unlikely(tsd == NULL)) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* The tsd_fast() / initialized checks are folded into the branch
|
||||
* testing (deallocated_after >= threshold) later in this function.
|
||||
* The threshold will be set to 0 when !tsd_fast.
|
||||
*/
|
||||
assert(tsd_fast(tsd)
|
||||
|| *tsd_thread_deallocated_next_event_fastp_get_unsafe(tsd) == 0);
|
||||
|
||||
emap_alloc_ctx_t alloc_ctx JEMALLOC_CC_SILENCE_INIT({0, 0, false});
|
||||
size_t usize;
|
||||
if (!size_hint) {
|
||||
bool err = emap_alloc_ctx_try_lookup_fast(
|
||||
tsd, &arena_emap_global, ptr, &alloc_ctx);
|
||||
|
||||
/* Note: profiled objects will have alloc_ctx.slab set */
|
||||
if (unlikely(err || !alloc_ctx.slab
|
||||
|| free_fastpath_nonfast_aligned(ptr,
|
||||
/* check_prof */ false))) {
|
||||
return false;
|
||||
}
|
||||
assert(alloc_ctx.szind != SC_NSIZES);
|
||||
usize = sz_index2size(alloc_ctx.szind);
|
||||
} else {
|
||||
/*
|
||||
* Check for both sizes that are too large, and for sampled /
|
||||
* special aligned objects. The alignment check will also check
|
||||
* for null ptr.
|
||||
*/
|
||||
if (unlikely(size > SC_LOOKUP_MAXCLASS
|
||||
|| free_fastpath_nonfast_aligned(ptr,
|
||||
/* check_prof */ true))) {
|
||||
return false;
|
||||
}
|
||||
sz_size2index_usize_fastpath(size, &alloc_ctx.szind, &usize);
|
||||
/* Max lookup class must be small. */
|
||||
assert(alloc_ctx.szind < SC_NBINS);
|
||||
/* This is a dead store, except when opt size checking is on. */
|
||||
alloc_ctx.slab = true;
|
||||
}
|
||||
/*
|
||||
* Currently the fastpath only handles small sizes. The branch on
|
||||
* SC_LOOKUP_MAXCLASS makes sure of it. This lets us avoid checking
|
||||
* tcache szind upper limit (i.e. tcache_max) as well.
|
||||
*/
|
||||
assert(alloc_ctx.slab);
|
||||
|
||||
uint64_t deallocated, threshold;
|
||||
te_free_fastpath_ctx(tsd, &deallocated, &threshold);
|
||||
|
||||
uint64_t deallocated_after = deallocated + usize;
|
||||
/*
|
||||
* Check for events and tsd non-nominal (fast_threshold will be set to
|
||||
* 0) in a single branch. Note that this handles the uninitialized case
|
||||
* as well (TSD init will be triggered on the non-fastpath). Therefore
|
||||
* anything depends on a functional TSD (e.g. the alloc_ctx sanity check
|
||||
* below) needs to be after this branch.
|
||||
*/
|
||||
if (unlikely(deallocated_after >= threshold)) {
|
||||
return false;
|
||||
}
|
||||
assert(tsd_fast(tsd));
|
||||
bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx);
|
||||
if (fail) {
|
||||
/* See the comment in isfree. */
|
||||
return true;
|
||||
}
|
||||
|
||||
tcache_t *tcache = tcache_get_from_ind(tsd, TCACHE_IND_AUTOMATIC,
|
||||
/* slow */ false, /* is_alloc */ false);
|
||||
cache_bin_t *bin = &tcache->bins[alloc_ctx.szind];
|
||||
|
||||
/*
|
||||
* If junking were enabled, this is where we would do it. It's not
|
||||
* though, since we ensured above that we're on the fast path. Assert
|
||||
* that to double-check.
|
||||
*/
|
||||
assert(!opt_junk_free);
|
||||
|
||||
if (!cache_bin_dalloc_easy(bin, ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*tsd_thread_deallocatedp_get(tsd) = deallocated_after;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void JEMALLOC_NOTHROW
|
||||
je_sdallocx_noflags(void *ptr, size_t size) {
|
||||
if (!free_fastpath(ptr, size, true)) {
|
||||
sdallocx_default(ptr, size, 0);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void JEMALLOC_NOTHROW
|
||||
je_sdallocx_impl(void *ptr, size_t size, int flags) {
|
||||
if (flags != 0 || !free_fastpath(ptr, size, true)) {
|
||||
sdallocx_default(ptr, size, flags);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void JEMALLOC_NOTHROW
|
||||
je_free_impl(void *ptr) {
|
||||
if (!free_fastpath(ptr, 0, false)) {
|
||||
free_default(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INLINES_C_H */
|
||||
|
|
|
|||
|
|
@ -2,42 +2,145 @@
|
|||
#define JEMALLOC_INTERNAL_MACROS_H
|
||||
|
||||
#ifdef JEMALLOC_DEBUG
|
||||
# define JEMALLOC_ALWAYS_INLINE static inline
|
||||
# define JEMALLOC_ALWAYS_INLINE static inline
|
||||
#else
|
||||
# define JEMALLOC_ALWAYS_INLINE JEMALLOC_ATTR(always_inline) static inline
|
||||
# ifdef _MSC_VER
|
||||
# define JEMALLOC_ALWAYS_INLINE static __forceinline
|
||||
# else
|
||||
# define JEMALLOC_ALWAYS_INLINE \
|
||||
JEMALLOC_ATTR(always_inline) static inline
|
||||
# endif
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
# define inline _inline
|
||||
# define inline _inline
|
||||
#endif
|
||||
|
||||
#define UNUSED JEMALLOC_ATTR(unused)
|
||||
|
||||
#define ZU(z) ((size_t)z)
|
||||
#define ZD(z) ((ssize_t)z)
|
||||
#define QU(q) ((uint64_t)q)
|
||||
#define QD(q) ((int64_t)q)
|
||||
#define ZU(z) ((size_t)z)
|
||||
#define ZD(z) ((ssize_t)z)
|
||||
#define QU(q) ((uint64_t)q)
|
||||
#define QD(q) ((int64_t)q)
|
||||
|
||||
#define KZU(z) ZU(z##ULL)
|
||||
#define KZD(z) ZD(z##LL)
|
||||
#define KQU(q) QU(q##ULL)
|
||||
#define KQD(q) QI(q##LL)
|
||||
#define KZU(z) ZU(z##ULL)
|
||||
#define KZD(z) ZD(z##LL)
|
||||
#define KQU(q) QU(q##ULL)
|
||||
#define KQD(q) QI(q##LL)
|
||||
|
||||
#ifndef __DECONST
|
||||
# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
|
||||
# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
|
||||
#endif
|
||||
|
||||
#if !defined(JEMALLOC_HAS_RESTRICT) || defined(__cplusplus)
|
||||
# define restrict
|
||||
# define restrict
|
||||
#endif
|
||||
|
||||
/* Various function pointers are statick and immutable except during testing. */
|
||||
/* Various function pointers are static and immutable except during testing. */
|
||||
#ifdef JEMALLOC_JET
|
||||
# define JET_MUTABLE
|
||||
# define JET_MUTABLE
|
||||
# define JET_EXTERN extern
|
||||
#else
|
||||
# define JET_MUTABLE const
|
||||
# define JET_MUTABLE const
|
||||
# define JET_EXTERN static
|
||||
#endif
|
||||
|
||||
#define JEMALLOC_VA_ARGS_HEAD(head, ...) head
|
||||
#define JEMALLOC_VA_ARGS_TAIL(head, ...) __VA_ARGS__
|
||||
|
||||
/* Diagnostic suppression macros */
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define JEMALLOC_DIAGNOSTIC_PUSH __pragma(warning(push))
|
||||
# define JEMALLOC_DIAGNOSTIC_POP __pragma(warning(pop))
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE(W) __pragma(warning(disable : W))
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_FRAME_ADDRESS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_DEPRECATED
|
||||
# define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
/* #pragma GCC diagnostic first appeared in gcc 4.6. */
|
||||
#elif (defined(__GNUC__) \
|
||||
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) \
|
||||
|| defined(__clang__)
|
||||
/*
|
||||
* The JEMALLOC_PRAGMA__ macro is an implementation detail of the GCC and Clang
|
||||
* diagnostic suppression macros and should not be used anywhere else.
|
||||
*/
|
||||
# define JEMALLOC_PRAGMA__(X) _Pragma(#X)
|
||||
# define JEMALLOC_DIAGNOSTIC_PUSH JEMALLOC_PRAGMA__(GCC diagnostic push)
|
||||
# define JEMALLOC_DIAGNOSTIC_POP JEMALLOC_PRAGMA__(GCC diagnostic pop)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE(W) \
|
||||
JEMALLOC_PRAGMA__(GCC diagnostic ignored W)
|
||||
|
||||
/*
|
||||
* The -Wmissing-field-initializers warning is buggy in GCC versions < 5.1 and
|
||||
* all clang versions up to version 7 (currently trunk, unreleased). This macro
|
||||
* suppresses the warning for the affected compiler versions only.
|
||||
*/
|
||||
# if ((defined(__GNUC__) && !defined(__clang__)) && (__GNUC__ < 5)) \
|
||||
|| defined(__clang__)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE( \
|
||||
"-Wmissing-field-initializers")
|
||||
# else
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
# endif
|
||||
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_FRAME_ADDRESS \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Wframe-address")
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Wtype-limits")
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_UNUSED_PARAMETER \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Wunused-parameter")
|
||||
# if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 7)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Walloc-size-larger-than=")
|
||||
# else
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
|
||||
# endif
|
||||
# ifdef JEMALLOC_HAVE_ATTR_DEPRECATED
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_DEPRECATED \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Wdeprecated-declarations")
|
||||
# else
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_DEPRECATED
|
||||
# endif
|
||||
# define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS \
|
||||
JEMALLOC_DIAGNOSTIC_PUSH \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE_UNUSED_PARAMETER
|
||||
#else
|
||||
# define JEMALLOC_DIAGNOSTIC_PUSH
|
||||
# define JEMALLOC_DIAGNOSTIC_POP
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE(W)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_FRAME_ADDRESS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_DEPRECATED
|
||||
# define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
#endif
|
||||
|
||||
#ifdef __clang_analyzer__
|
||||
# define JEMALLOC_CLANG_ANALYZER
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_CLANG_ANALYZER
|
||||
# define JEMALLOC_CLANG_ANALYZER_SUPPRESS __attribute__((suppress))
|
||||
# define JEMALLOC_CLANG_ANALYZER_SILENCE_INIT(v) = v
|
||||
#else
|
||||
# define JEMALLOC_CLANG_ANALYZER_SUPPRESS
|
||||
# define JEMALLOC_CLANG_ANALYZER_SILENCE_INIT(v)
|
||||
#endif
|
||||
|
||||
#define JEMALLOC_SUPPRESS_WARN_ON_USAGE(...) \
|
||||
JEMALLOC_DIAGNOSTIC_PUSH \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE_DEPRECATED \
|
||||
__VA_ARGS__ \
|
||||
JEMALLOC_DIAGNOSTIC_POP
|
||||
|
||||
/*
|
||||
* Disables spurious diagnostics for all headers. Since these headers are not
|
||||
* included by users directly, it does not affect their diagnostic settings.
|
||||
*/
|
||||
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_MACROS_H */
|
||||
|
|
|
|||
22
include/jemalloc/internal/jemalloc_internal_overrides.h
Normal file
22
include/jemalloc/internal/jemalloc_internal_overrides.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef JEMALLOC_INTERNAL_OVERRIDES_H
|
||||
#define JEMALLOC_INTERNAL_OVERRIDES_H
|
||||
|
||||
/*
|
||||
* Under normal circumstances this header serves no purpose, as these settings
|
||||
* can be customized via the corresponding autoconf options at configure-time.
|
||||
* Overriding in this fashion is useful when the header files generated by
|
||||
* autoconf are used as input for another build system.
|
||||
*/
|
||||
|
||||
#ifdef JEMALLOC_OVERRIDE_LG_PAGE
|
||||
# undef LG_PAGE
|
||||
# define LG_PAGE JEMALLOC_OVERRIDE_LG_PAGE
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_OVERRIDE_JEMALLOC_CONFIG_MALLOC_CONF
|
||||
# undef JEMALLOC_CONFIG_MALLOC_CONF
|
||||
# define JEMALLOC_CONFIG_MALLOC_CONF \
|
||||
JEMALLOC_OVERRIDE_JEMALLOC_CONFIG_MALLOC_CONF
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_OVERRIDES_H */
|
||||
|
|
@ -1,15 +1,33 @@
|
|||
#ifndef JEMALLOC_INTERNAL_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_TYPES_H
|
||||
|
||||
/* Page size index type. */
|
||||
typedef unsigned pszind_t;
|
||||
|
||||
/* Size class index type. */
|
||||
typedef unsigned szind_t;
|
||||
#include "jemalloc/internal/quantum.h"
|
||||
|
||||
/* Processor / core id type. */
|
||||
typedef int malloc_cpuid_t;
|
||||
|
||||
/* When realloc(non-null-ptr, 0) is called, what happens? */
|
||||
enum zero_realloc_action_e {
|
||||
/* Realloc(ptr, 0) is free(ptr); return malloc(0); */
|
||||
zero_realloc_action_alloc = 0,
|
||||
/* Realloc(ptr, 0) is free(ptr); */
|
||||
zero_realloc_action_free = 1,
|
||||
/* Realloc(ptr, 0) aborts. */
|
||||
zero_realloc_action_abort = 2
|
||||
};
|
||||
typedef enum zero_realloc_action_e zero_realloc_action_t;
|
||||
|
||||
/* Signature of write callback. */
|
||||
typedef void(write_cb_t)(void *, const char *);
|
||||
|
||||
enum malloc_init_e {
|
||||
malloc_init_uninitialized = 3,
|
||||
malloc_init_a0_initialized = 2,
|
||||
malloc_init_recursible = 1,
|
||||
malloc_init_initialized = 0 /* Common case --> jnz. */
|
||||
};
|
||||
typedef enum malloc_init_e malloc_init_t;
|
||||
|
||||
/*
|
||||
* Flags bits:
|
||||
*
|
||||
|
|
@ -21,121 +39,46 @@ typedef int malloc_cpuid_t;
|
|||
*
|
||||
* aaaaaaaa aaaatttt tttttttt 0znnnnnn
|
||||
*/
|
||||
#define MALLOCX_ARENA_BITS 12
|
||||
#define MALLOCX_TCACHE_BITS 12
|
||||
#define MALLOCX_LG_ALIGN_BITS 6
|
||||
#define MALLOCX_ARENA_SHIFT 20
|
||||
#define MALLOCX_TCACHE_SHIFT 8
|
||||
#define MALLOCX_ARENA_MASK \
|
||||
(((1 << MALLOCX_ARENA_BITS) - 1) << MALLOCX_ARENA_SHIFT)
|
||||
#define MALLOCX_ARENA_BITS 12
|
||||
#define MALLOCX_TCACHE_BITS 12
|
||||
#define MALLOCX_LG_ALIGN_BITS 6
|
||||
#define MALLOCX_ARENA_SHIFT 20
|
||||
#define MALLOCX_TCACHE_SHIFT 8
|
||||
#define MALLOCX_ARENA_MASK \
|
||||
((unsigned)(((1U << MALLOCX_ARENA_BITS) - 1) << MALLOCX_ARENA_SHIFT))
|
||||
/* NB: Arena index bias decreases the maximum number of arenas by 1. */
|
||||
#define MALLOCX_ARENA_LIMIT ((1 << MALLOCX_ARENA_BITS) - 1)
|
||||
#define MALLOCX_TCACHE_MASK \
|
||||
(((1 << MALLOCX_TCACHE_BITS) - 1) << MALLOCX_TCACHE_SHIFT)
|
||||
#define MALLOCX_TCACHE_MAX ((1 << MALLOCX_TCACHE_BITS) - 3)
|
||||
#define MALLOCX_LG_ALIGN_MASK ((1 << MALLOCX_LG_ALIGN_BITS) - 1)
|
||||
#define MALLOCX_ARENA_LIMIT ((unsigned)((1U << MALLOCX_ARENA_BITS) - 1))
|
||||
#define MALLOCX_TCACHE_MASK \
|
||||
((unsigned)(((1U << MALLOCX_TCACHE_BITS) - 1) << MALLOCX_TCACHE_SHIFT))
|
||||
#define MALLOCX_TCACHE_MAX ((unsigned)((1U << MALLOCX_TCACHE_BITS) - 3))
|
||||
#define MALLOCX_LG_ALIGN_MASK ((1 << MALLOCX_LG_ALIGN_BITS) - 1)
|
||||
/* Use MALLOCX_ALIGN_GET() if alignment may not be specified in flags. */
|
||||
#define MALLOCX_ALIGN_GET_SPECIFIED(flags) \
|
||||
(ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK))
|
||||
#define MALLOCX_ALIGN_GET(flags) \
|
||||
(MALLOCX_ALIGN_GET_SPECIFIED(flags) & (SIZE_T_MAX-1))
|
||||
#define MALLOCX_ZERO_GET(flags) \
|
||||
((bool)(flags & MALLOCX_ZERO))
|
||||
#define MALLOCX_ALIGN_GET_SPECIFIED(flags) \
|
||||
(ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK))
|
||||
#define MALLOCX_ALIGN_GET(flags) \
|
||||
(MALLOCX_ALIGN_GET_SPECIFIED(flags) & (SIZE_T_MAX - 1))
|
||||
#define MALLOCX_ZERO_GET(flags) ((bool)(flags & MALLOCX_ZERO))
|
||||
|
||||
#define MALLOCX_TCACHE_GET(flags) \
|
||||
(((unsigned)((flags & MALLOCX_TCACHE_MASK) >> MALLOCX_TCACHE_SHIFT)) - 2)
|
||||
#define MALLOCX_ARENA_GET(flags) \
|
||||
(((unsigned)(((unsigned)flags) >> MALLOCX_ARENA_SHIFT)) - 1)
|
||||
#define MALLOCX_TCACHE_GET(flags) \
|
||||
(((unsigned)((flags & MALLOCX_TCACHE_MASK) >> MALLOCX_TCACHE_SHIFT)) \
|
||||
- 2)
|
||||
#define MALLOCX_ARENA_GET(flags) \
|
||||
(((unsigned)(((unsigned)flags) >> MALLOCX_ARENA_SHIFT)) - 1)
|
||||
|
||||
/* Smallest size class to support. */
|
||||
#define TINY_MIN (1U << LG_TINY_MIN)
|
||||
#define TINY_MIN (1U << LG_TINY_MIN)
|
||||
|
||||
/*
|
||||
* Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
|
||||
* classes).
|
||||
*/
|
||||
#ifndef LG_QUANTUM
|
||||
# if (defined(__i386__) || defined(_M_IX86))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __ia64__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __alpha__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__sparc64__) || defined(__sparcv9) || defined(__sparc_v9__))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__amd64__) || defined(__x86_64__) || defined(_M_X64))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __arm__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __aarch64__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __hppa__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __m68k__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __mips__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __nios2__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __or1k__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __powerpc__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if defined(__riscv) || defined(__riscv__)
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __s390__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined (__SH3E__) || defined(__SH4_SINGLE__) || defined(__SH4__) || \
|
||||
defined(__SH4_SINGLE_ONLY__))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __tile__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __le32__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifndef LG_QUANTUM
|
||||
# error "Unknown minimum alignment for architecture; specify via "
|
||||
"--with-lg-quantum"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define QUANTUM ((size_t)(1U << LG_QUANTUM))
|
||||
#define QUANTUM_MASK (QUANTUM - 1)
|
||||
|
||||
/* Return the smallest quantum multiple that is >= a. */
|
||||
#define QUANTUM_CEILING(a) \
|
||||
(((a) + QUANTUM_MASK) & ~QUANTUM_MASK)
|
||||
|
||||
#define LONG ((size_t)(1U << LG_SIZEOF_LONG))
|
||||
#define LONG_MASK (LONG - 1)
|
||||
#define LONG ((size_t)(1U << LG_SIZEOF_LONG))
|
||||
#define LONG_MASK (LONG - 1)
|
||||
|
||||
/* Return the smallest long multiple that is >= a. */
|
||||
#define LONG_CEILING(a) \
|
||||
(((a) + LONG_MASK) & ~LONG_MASK)
|
||||
#define LONG_CEILING(a) (((a) + LONG_MASK) & ~LONG_MASK)
|
||||
|
||||
#define SIZEOF_PTR (1U << LG_SIZEOF_PTR)
|
||||
#define PTR_MASK (SIZEOF_PTR - 1)
|
||||
#define SIZEOF_PTR (1U << LG_SIZEOF_PTR)
|
||||
#define PTR_MASK (SIZEOF_PTR - 1)
|
||||
|
||||
/* Return the smallest (void *) multiple that is >= a. */
|
||||
#define PTR_CEILING(a) \
|
||||
(((a) + PTR_MASK) & ~PTR_MASK)
|
||||
#define PTR_CEILING(a) (((a) + PTR_MASK) & ~PTR_MASK)
|
||||
|
||||
/*
|
||||
* Maximum size of L1 cache line. This is used to avoid cache line aliasing.
|
||||
|
|
@ -144,42 +87,62 @@ typedef int malloc_cpuid_t;
|
|||
* CACHELINE cannot be based on LG_CACHELINE because __declspec(align()) can
|
||||
* only handle raw constants.
|
||||
*/
|
||||
#define LG_CACHELINE 6
|
||||
#define CACHELINE 64
|
||||
#define CACHELINE_MASK (CACHELINE - 1)
|
||||
#define LG_CACHELINE 6
|
||||
#define CACHELINE 64
|
||||
#define CACHELINE_MASK (CACHELINE - 1)
|
||||
|
||||
/* Return the smallest cacheline multiple that is >= s. */
|
||||
#define CACHELINE_CEILING(s) \
|
||||
(((s) + CACHELINE_MASK) & ~CACHELINE_MASK)
|
||||
#define CACHELINE_CEILING(s) (((s) + CACHELINE_MASK) & ~CACHELINE_MASK)
|
||||
|
||||
/* Return the nearest aligned address at or below a. */
|
||||
#define ALIGNMENT_ADDR2BASE(a, alignment) \
|
||||
((void *)((uintptr_t)(a) & ((~(alignment)) + 1)))
|
||||
#define ALIGNMENT_ADDR2BASE(a, alignment) \
|
||||
((void *)(((byte_t *)(a)) \
|
||||
- (((uintptr_t)(a)) - ((uintptr_t)(a) & ((~(alignment)) + 1)))))
|
||||
|
||||
/* Return the offset between a and the nearest aligned address at or below a. */
|
||||
#define ALIGNMENT_ADDR2OFFSET(a, alignment) \
|
||||
#define ALIGNMENT_ADDR2OFFSET(a, alignment) \
|
||||
((size_t)((uintptr_t)(a) & (alignment - 1)))
|
||||
|
||||
/* Return the smallest alignment multiple that is >= s. */
|
||||
#define ALIGNMENT_CEILING(s, alignment) \
|
||||
#define ALIGNMENT_CEILING(s, alignment) \
|
||||
(((s) + (alignment - 1)) & ((~(alignment)) + 1))
|
||||
|
||||
/*
|
||||
* Return the nearest aligned address at or above a.
|
||||
*
|
||||
* While at first glance this would appear to be merely a more complicated
|
||||
* way to perform the same computation as `ALIGNMENT_CEILING`,
|
||||
* this has the important additional property of not concealing pointer
|
||||
* provenance from the compiler. See the block-comment on the
|
||||
* definition of `byte_t` for more details.
|
||||
*/
|
||||
#define ALIGNMENT_ADDR2CEILING(a, alignment) \
|
||||
((void *)(((byte_t *)(a)) \
|
||||
+ (((((uintptr_t)(a)) + (alignment - 1)) & ((~(alignment)) + 1)) \
|
||||
- ((uintptr_t)(a)))))
|
||||
|
||||
/* Declare a variable-length array. */
|
||||
#if __STDC_VERSION__ < 199901L
|
||||
# ifdef _MSC_VER
|
||||
# include <malloc.h>
|
||||
# define alloca _alloca
|
||||
# else
|
||||
# ifdef JEMALLOC_HAS_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
# endif
|
||||
# define VARIABLE_ARRAY(type, name, count) \
|
||||
type *name = alloca(sizeof(type) * (count))
|
||||
#if __STDC_VERSION__ < 199901L || defined(__STDC_NO_VLA__)
|
||||
# ifdef _MSC_VER
|
||||
# include <malloc.h>
|
||||
# define alloca _alloca
|
||||
# else
|
||||
# ifdef JEMALLOC_HAS_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
# endif
|
||||
# define VARIABLE_ARRAY_UNSAFE(type, name, count) \
|
||||
type *name = alloca(sizeof(type) * (count))
|
||||
#else
|
||||
# define VARIABLE_ARRAY(type, name, count) type name[(count)]
|
||||
# define VARIABLE_ARRAY_UNSAFE(type, name, count) type name[(count)]
|
||||
#endif
|
||||
#define VARIABLE_ARRAY_SIZE_MAX 2048
|
||||
#define VARIABLE_ARRAY(type, name, count) \
|
||||
assert(sizeof(type) * (count) <= VARIABLE_ARRAY_SIZE_MAX); \
|
||||
VARIABLE_ARRAY_UNSAFE(type, name, count)
|
||||
|
||||
#define CALLOC_MADVISE_THRESHOLD_DEFAULT (((size_t)1) << 23) /* 8 MB */
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_TYPES_H */
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue