Skip to content

Commit 538b440

Browse files
Refactor: Introduce SmapsParser
Signed-off-by: jonghoonpark <dev@jonghoonpark.com>
1 parent 5271448 commit 538b440

3 files changed

Lines changed: 248 additions & 254 deletions

File tree

test/hotspot/jtreg/gc/TestTransparentHugePagesHeap.java

Lines changed: 17 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,7 @@
4949
* @run driver TestTransparentHugePagesHeap Serial
5050
*/
5151

52-
import java.math.BigInteger;
53-
import java.nio.file.Files;
54-
import java.nio.file.Path;
5552
import java.nio.file.Paths;
56-
import java.nio.file.StandardCopyOption;
5753
import java.util.regex.Matcher;
5854
import java.util.regex.Pattern;
5955
import java.util.Scanner;
@@ -62,6 +58,7 @@
6258
import jdk.test.lib.process.OutputAnalyzer;
6359
import jdk.test.lib.process.ProcessTools;
6460
import jdk.test.lib.Platform;
61+
import jdk.test.lib.SmapsParser;
6562

6663
import jtreg.SkippedException;
6764

@@ -100,82 +97,38 @@ class VerifyTHPEnabledForHeap {
10097

10198
public static void main(String args[]) throws Exception {
10299
// Extract the heap start from pagesize logging
103-
BigInteger heapStart = extractHeapStartFromLog();
100+
String heapStart = extractHeapStartFromLog();
104101

105-
Path smaps = makeSmapsCopy();
102+
SmapsParser smapsParser = new SmapsParser();
103+
smapsParser.parse();
106104

107-
final Pattern addressRangePattern = Pattern.compile("([0-9a-f]*?)-([0-9a-f]*?) .*");
108-
final Pattern thpEligiblePattern = Pattern.compile("THPeligible:\\s+(\\d)\\s*");
109-
110-
Scanner smapsFile = new Scanner(smaps);
111-
while (smapsFile.hasNextLine()) {
112-
Matcher addressRangeMatcher = addressRangePattern.matcher(smapsFile.nextLine());
113-
if (!addressRangeMatcher.matches()) {
114-
continue;
115-
}
116-
117-
// Found an address range line in the smaps file
118-
119-
BigInteger addressStart = new BigInteger(addressRangeMatcher.group(1), 16);
120-
BigInteger addressEnd = new BigInteger(addressRangeMatcher.group(2), 16);
121-
122-
// Linux sometimes merges adjacent VMAs so we can't search for a range that
123-
// exactly matches the heap range. Instead we look for the first range that
124-
// contains the start of the heap and verify that that range is THP eligible.
125-
126-
if (addressStart.compareTo(heapStart) > 0 || heapStart.compareTo(addressEnd) >= 0) {
127-
continue;
128-
}
129-
130-
// Found a range that contains the start of the heap, verify that it is THP eligible.
131-
132-
while (smapsFile.hasNextLine()) {
133-
Matcher m = thpEligiblePattern.matcher(smapsFile.nextLine());
134-
if (!m.matches()) {
135-
continue;
136-
}
137-
138-
// Found the THPeligible line
139-
140-
if (m.group(1).equals("1")) {
141-
// Success - THPeligible is 1, heap can be backed by huge pages
142-
return;
143-
}
144-
145-
throw new RuntimeException("The address range 0x" + addressStart.toString(16)
146-
+ "-0x" + addressEnd.toString(16)
147-
+ " that contains the heap start" + heapStart
148-
+ " is not THPeligible");
149-
}
150-
151-
throw new RuntimeException("Couldn't find THPeligible in the smaps file");
105+
SmapsParser.RangeWithPageSize range = smapsParser.getRange(heapStart);
106+
if (range == null) {
107+
throw new AssertionError("Could not find heap section in smaps file. No memory range found for heap start: " + heapStart);
152108
}
153109

154-
throw new RuntimeException("Could not find an address range containing the heap start " + heapStart + " in the smaps file");
110+
if (!range.isTransparentHuge()) {
111+
// Failed to verify THP for heap
112+
throw new RuntimeException("The address range 0x" + range.getStart().toString(16)
113+
+ "-0x" + range.getEnd().toString(16)
114+
+ " that contains the heap start" + heapStart
115+
+ " is not THPeligible");
116+
}
155117
}
156118

157-
private static BigInteger extractHeapStartFromLog() throws Exception {
119+
private static String extractHeapStartFromLog() throws Exception {
158120
// [0.041s][info][pagesize] Heap: min=128M max=128M base=0x0000ffff5c600000 size=128M page_size=2M
159121
final Pattern heapAddress = Pattern.compile(".* Heap: .*base=0x([0-9A-Fa-f]*).*");
160122

161123
Scanner logFile = new Scanner(Paths.get("thp-" + ProcessHandle.current().pid() + ".log"));
162124
while (logFile.hasNextLine()) {
163-
String line = logFile.nextLine();
164-
165-
Matcher m = heapAddress.matcher(line);
125+
Matcher m = heapAddress.matcher(logFile.nextLine());
166126
if (m.matches()) {
167-
return new BigInteger(m.group(1), 16);
127+
return m.group(1);
168128
}
169129
}
170130

171131
throw new RuntimeException("Failed to find heap start");
172132
}
173-
174-
private static Path makeSmapsCopy() throws Exception {
175-
Path src = Paths.get("/proc/self/smaps");
176-
Path dest = Paths.get("smaps-copy-" + ProcessHandle.current().pid() + ".txt");
177-
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
178-
return dest;
179-
}
180133
}
181134
}

test/hotspot/jtreg/runtime/os/TestTracePageSizes.java

Lines changed: 5 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -104,135 +104,21 @@
104104
*/
105105

106106
import java.io.File;
107-
import java.nio.file.Files;
108-
import java.nio.file.Path;
109-
import java.nio.file.Paths;
110-
import java.nio.file.StandardCopyOption;
111-
import java.util.LinkedList;
112107
import java.util.Scanner;
113108
import java.util.regex.Matcher;
114109
import java.util.regex.Pattern;
115110
import jdk.test.lib.Platform;
111+
import jdk.test.lib.SmapsParser;
112+
import jdk.test.lib.SmapsParser.RangeWithPageSize;
116113
import jtreg.SkippedException;
117114

118115
// Check that page sizes logged match what is recorded in /proc/self/smaps.
119116
// For transparent huge pages the matching is best effort since we can't
120117
// know for sure what the underlying page size is.
121118
public class TestTracePageSizes {
122-
// Store address ranges with known page size.
123-
private static LinkedList<RangeWithPageSize> ranges = new LinkedList<>();
124119
private static boolean debug;
125120
private static int run;
126121

127-
// Copy smaps locally
128-
// (To minimize chances of concurrent modification when parsing, as well as helping with error analysis)
129-
private static Path copySmaps() throws Exception {
130-
Path p1 = Paths.get("/proc/self/smaps");
131-
Path p2 = Paths.get("smaps-copy-" + ProcessHandle.current().pid() + "-" + (run++) + ".txt");
132-
Files.copy(p1, p2, StandardCopyOption.REPLACE_EXISTING);
133-
debug("Copied " + p1 + " to " + p2 + "...");
134-
return p2;
135-
}
136-
137-
// Parse /proc/self/smaps.
138-
private static void parseSmaps() throws Exception {
139-
// We can override the smaps file to parse to pass in a pre-fetched one
140-
String smapsFileToParse = System.getProperty("smaps-file");
141-
if (smapsFileToParse != null) {
142-
parseSmaps(Paths.get(smapsFileToParse));
143-
} else {
144-
Path smapsCopy = copySmaps();
145-
parseSmaps(smapsCopy);
146-
}
147-
}
148-
149-
static class SmapsParser {
150-
// This is a simple smaps parser; it will recognize smaps section start lines
151-
// (e.g. "40fa00000-439b80000 rw-p 00000000 00:00 0 ") and look for keywords inside the section.
152-
// Section will be finished and written into a RangeWithPageSize when either the next section is found
153-
// or the end of file is encountered.
154-
static final Pattern SECTION_START_PATT = Pattern.compile("^([a-f0-9]+)-([a-f0-9]+) [\\-rwpsx]{4}.*");
155-
static final Pattern KERNEL_PAGESIZE_PATT = Pattern.compile("^KernelPageSize:\\s*(\\d*) kB");
156-
static final Pattern THP_ELIGIBLE_PATT = Pattern.compile("^THPeligible:\\s+(\\d*)");
157-
static final Pattern VMFLAGS_PATT = Pattern.compile("^VmFlags: ([\\w\\? ]*)");
158-
String start;
159-
String end;
160-
String ps;
161-
String thpEligible;
162-
String vmFlags;
163-
int lineno;
164-
165-
void reset() {
166-
start = null;
167-
end = null;
168-
ps = null;
169-
thpEligible = null;
170-
vmFlags = null;
171-
}
172-
173-
public void finish() {
174-
if (start != null) {
175-
RangeWithPageSize range = new RangeWithPageSize(start, end, ps, thpEligible, vmFlags);
176-
ranges.add(range);
177-
debug("Added range: " + range);
178-
reset();
179-
}
180-
}
181-
182-
void eatNext(String line) {
183-
// For better debugging experience call finish here before the debug() call.
184-
Matcher matSectionStart = SECTION_START_PATT.matcher(line);
185-
if (matSectionStart.matches()) {
186-
finish();
187-
}
188-
189-
debug("" + (lineno++) + " " + line);
190-
191-
if (matSectionStart.matches()) {
192-
start = matSectionStart.group(1);
193-
end = matSectionStart.group(2);
194-
ps = null;
195-
vmFlags = null;
196-
return;
197-
} else {
198-
Matcher matKernelPageSize = KERNEL_PAGESIZE_PATT.matcher(line);
199-
if (matKernelPageSize.matches()) {
200-
ps = matKernelPageSize.group(1);
201-
return;
202-
}
203-
Matcher matTHPEligible = THP_ELIGIBLE_PATT.matcher(line);
204-
if (matTHPEligible.matches()) {
205-
thpEligible = matTHPEligible.group(1);
206-
return;
207-
}
208-
Matcher matVmFlags = VMFLAGS_PATT.matcher(line);
209-
if (matVmFlags.matches()) {
210-
vmFlags = matVmFlags.group(1);
211-
return;
212-
}
213-
}
214-
}
215-
}
216-
217-
// Parse /proc/self/smaps
218-
private static void parseSmaps(Path smapsFileToParse) throws Exception {
219-
debug("Parsing: " + smapsFileToParse.getFileName() + "...");
220-
SmapsParser parser = new SmapsParser();
221-
Files.lines(smapsFileToParse).forEach(parser::eatNext);
222-
parser.finish();
223-
}
224-
225-
// Search for a range including the given address.
226-
private static RangeWithPageSize getRange(String addr) {
227-
long laddr = Long.decode(addr);
228-
for (RangeWithPageSize range : ranges) {
229-
if (range.includes(laddr)) {
230-
return range;
231-
}
232-
}
233-
return null;
234-
}
235-
236122
// Helper to get the page size in KB given a page size parsed
237123
// from log_info(pagesize) output.
238124
private static long pageSizeInKB(String pageSize) {
@@ -272,7 +158,8 @@ public static void main(String args[]) throws Exception {
272158
}
273159

274160
// Parse /proc/self/smaps to compare with values logged in the VM.
275-
parseSmaps();
161+
SmapsParser smapsParser = new SmapsParser();
162+
smapsParser.parse("-" + (run++));
276163

277164
// Setup patters for the JVM page size logging.
278165
String traceLinePatternString = ".*base=(0x[0-9A-Fa-f]*).* page_size=(\\d+[BKMG]).*";
@@ -289,7 +176,7 @@ public static void main(String args[]) throws Exception {
289176
String address = trace.group(1);
290177
String pageSize = trace.group(2);
291178

292-
RangeWithPageSize range = getRange(address);
179+
RangeWithPageSize range = smapsParser.getRange(address);
293180
if (range == null) {
294181
debug("Could not find range for: " + line);
295182
throw new AssertionError("No memory range found for address: " + address);
@@ -325,75 +212,3 @@ private static void debug(String str) {
325212
}
326213
}
327214
}
328-
329-
// Class used to store information about memory ranges parsed
330-
// from /proc/self/smaps. The file contain a lot of information
331-
// about the different mappings done by an application, but the
332-
// lines we care about are:
333-
// 700000000-73ea00000 rw-p 00000000 00:00 0
334-
// ...
335-
// KernelPageSize: 4 kB
336-
// ...
337-
// VmFlags: rd wr mr mw me ac sd
338-
//
339-
// We use the VmFlags to know what kind of huge pages are used.
340-
// For transparent huge pages the KernelPageSize field will not
341-
// report the large page size.
342-
class RangeWithPageSize {
343-
private long start;
344-
private long end;
345-
private long pageSize;
346-
private boolean thpEligible;
347-
private boolean vmFlagHG;
348-
private boolean vmFlagHT;
349-
private boolean isTHP;
350-
351-
public RangeWithPageSize(String start, String end, String pageSize, String thpEligible, String vmFlags) {
352-
// Note: since we insist on kernels >= 3.8, all the following information should be present
353-
// (none of the input strings be null).
354-
this.start = Long.parseUnsignedLong(start, 16);
355-
this.end = Long.parseUnsignedLong(end, 16);
356-
this.pageSize = Long.parseLong(pageSize);
357-
this.thpEligible = thpEligible == null ? false : (Integer.parseInt(thpEligible) == 1);
358-
359-
vmFlagHG = false;
360-
vmFlagHT = false;
361-
// Check if the vmFlags line include:
362-
// * ht - Meaning the range is mapped using explicit huge pages.
363-
// * hg - Meaning the range is madvised huge.
364-
for (String flag : vmFlags.split(" ")) {
365-
if (flag.equals("ht")) {
366-
vmFlagHT = true;
367-
} else if (flag.equals("hg")) {
368-
vmFlagHG = true;
369-
}
370-
}
371-
372-
// When the THP policy is 'always' instead of 'madvise, the vmFlagHG property is false,
373-
// therefore also check thpEligible. If this is still causing problems in the future,
374-
// we might have to check the AnonHugePages field.
375-
376-
isTHP = vmFlagHG || this.thpEligible;
377-
}
378-
379-
public long getPageSize() {
380-
return pageSize;
381-
}
382-
383-
public boolean isTransparentHuge() {
384-
return isTHP;
385-
}
386-
387-
public boolean isExplicitHuge() {
388-
return vmFlagHT;
389-
}
390-
391-
public boolean includes(long addr) {
392-
return start <= addr && addr < end;
393-
}
394-
395-
public String toString() {
396-
return "[" + Long.toHexString(start) + ", " + Long.toHexString(end) + ") " +
397-
"pageSize=" + pageSize + "KB isTHP=" + isTHP + " isHUGETLB=" + vmFlagHT;
398-
}
399-
}

0 commit comments

Comments
 (0)