Skip to content

Commit 039175f

Browse files
authored
Merge pull request #6122 from jdmpapin/string
Define compiler string formatting utilities
2 parents 0c72213 + cc0a9b8 commit 039175f

File tree

7 files changed

+316
-25
lines changed

7 files changed

+316
-25
lines changed

compiler/control/CompileMethod.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2020 IBM Corp. and others
2+
* Copyright (c) 2000, 2021 IBM Corp. and others
33
*
44
* This program and the accompanying materials are made available under
55
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -54,16 +54,13 @@
5454
#include "ilgen/IlGenRequest.hpp"
5555
#include "ilgen/IlGeneratorMethodDetails.hpp"
5656
#include "infra/Assert.hpp"
57+
#include "infra/String.hpp"
5758
#include "ras/Debug.hpp"
5859
#include "env/SystemSegmentProvider.hpp"
5960
#include "env/DebugSegmentProvider.hpp"
6061
#include "omrformatconsts.h"
6162
#include "runtime/CodeCacheManager.hpp"
6263

63-
#if defined (_MSC_VER) && _MSC_VER < 1900
64-
#define snprintf _snprintf
65-
#endif
66-
6764
static void
6865
writePerfToolEntry(void *start, uint32_t size, const char *name)
6966
{
@@ -82,8 +79,8 @@ writePerfToolEntry(void *start, uint32_t size, const char *name)
8279
static const int maxPerfFilenameSize = 15 + sizeof(jvmPid)* 3; // "/tmp/perf-%ld.map"
8380
char perfFilename[maxPerfFilenameSize] = { 0 };
8481

85-
int numCharsWritten = snprintf(perfFilename, maxPerfFilenameSize, "/tmp/perf-%" OMR_PRId64 ".map", static_cast<int64_t>(jvmPid));
86-
if (numCharsWritten > 0 && numCharsWritten < maxPerfFilenameSize)
82+
bool truncated = TR::snprintfTrunc(perfFilename, maxPerfFilenameSize, "/tmp/perf-%" OMR_PRId64 ".map", static_cast<int64_t>(jvmPid));
83+
if (!truncated)
8784
{
8885
perfFile = fopen(perfFilename, "a");
8986
}

compiler/ilgen/OMRMethodBuilder.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2016, 2020 IBM Corp. and others
2+
* Copyright (c) 2016, 2021 IBM Corp. and others
33
*
44
* This program and the accompanying materials are made available under
55
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -44,6 +44,7 @@
4444
#include "infra/Cfg.hpp"
4545
#include "infra/STLUtils.hpp"
4646
#include "infra/List.hpp"
47+
#include "infra/String.hpp"
4748
#include "ilgen/IlGeneratorMethodDetails_inlines.hpp"
4849
#include "ilgen/IlInjector.hpp"
4950
#include "ilgen/IlBuilder.hpp"
@@ -60,10 +61,6 @@
6061
#define TraceEnabled (comp()->getOption(TR_TraceILGen))
6162
#define TraceIL(m, ...) {if (TraceEnabled) {traceMsg(comp(), m, ##__VA_ARGS__);}}
6263

63-
#if defined (_MSC_VER) && _MSC_VER < 1900
64-
#define snprintf _snprintf
65-
#endif
66-
6764
// MethodBuilder is an IlBuilder object representing an entire method /
6865
// function, so it conceptually has an entry point (though multiple entry
6966
// method builders are entirely possible). Typically there is a single
@@ -492,13 +489,13 @@ OMR::MethodBuilder::isSymbolAnArray(const char *name)
492489
void
493490
OMR::MethodBuilder::DefineLine(const char *line)
494491
{
495-
snprintf(_definingLine, MAX_LINE_NUM_LEN * sizeof(char), "%s", line);
492+
TR::snprintfNoTrunc(_definingLine, MAX_LINE_NUM_LEN * sizeof(char), "%s", line);
496493
}
497494

498495
void
499496
OMR::MethodBuilder::DefineLine(int line)
500497
{
501-
snprintf(_definingLine, MAX_LINE_NUM_LEN * sizeof(char), "%d", line);
498+
TR::snprintfNoTrunc(_definingLine, MAX_LINE_NUM_LEN * sizeof(char), "%d", line);
502499
}
503500

504501
void

compiler/infra/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
###############################################################################
2-
# Copyright (c) 2017, 2018 IBM Corp. and others
2+
# Copyright (c) 2017, 2021 IBM Corp. and others
33
#
44
# This program and the accompanying materials are made available under
55
# the terms of the Eclipse Public License 2.0 which accompanies this
@@ -32,6 +32,7 @@ compiler_library(infra
3232
${CMAKE_CURRENT_LIST_DIR}/OMRMonitorTable.cpp
3333
${CMAKE_CURRENT_LIST_DIR}/Random.cpp
3434
${CMAKE_CURRENT_LIST_DIR}/SimpleRegex.cpp
35+
${CMAKE_CURRENT_LIST_DIR}/String.cpp
3536
${CMAKE_CURRENT_LIST_DIR}/STLUtils.cpp
3637
${CMAKE_CURRENT_LIST_DIR}/Timer.cpp
3738
${CMAKE_CURRENT_LIST_DIR}/TreeServices.cpp

compiler/infra/String.cpp

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2021, 2021 IBM Corp. and others
3+
*
4+
* This program and the accompanying materials are made available under
5+
* the terms of the Eclipse Public License 2.0 which accompanies this
6+
* distribution and is available at http://eclipse.org/legal/epl-2.0
7+
* or the Apache License, Version 2.0 which accompanies this distribution
8+
* and is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
*
10+
* This Source Code may also be made available under the following Secondary
11+
* Licenses when the conditions for such availability set forth in the
12+
* Eclipse Public License, v. 2.0 are satisfied: GNU General Public License,
13+
* version 2 with the GNU Classpath Exception [1] and GNU General Public
14+
* License, version 2 with the OpenJDK Assembly Exception [2].
15+
*
16+
* [1] https://www.gnu.org/software/classpath/license.html
17+
* [2] http://openjdk.java.net/legal/assembly-exception.html
18+
*
19+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
20+
*******************************************************************************/
21+
22+
#include "infra/String.hpp"
23+
#include "infra/Assert.hpp"
24+
#include "env/FrontEnd.hpp" // defines va_copy for old MSVC and z/OS
25+
#include <limits.h>
26+
27+
#if defined (_MSC_VER) && (_MSC_VER < 1900)
28+
#define stdlib_vsnprintf _vsnprintf
29+
#else
30+
#define stdlib_vsnprintf vsnprintf
31+
#endif
32+
33+
namespace TR {
34+
35+
// Returns the length of the formatted string without writing it to memory.
36+
int vprintfLen(const char *fmt, va_list args)
37+
{
38+
// This works with both standard vsnprintf() and _vsnprintf().
39+
return stdlib_vsnprintf(NULL, 0, fmt, args);
40+
}
41+
42+
// Returns the length of the formatted string without writing it to memory.
43+
int printfLen(const char *fmt, ...)
44+
{
45+
va_list args;
46+
va_start(args, fmt);
47+
int len = TR::vprintfLen(fmt, args);
48+
va_end(args);
49+
return len;
50+
}
51+
52+
// Returns true if the formatted string was truncated. The length of the
53+
// formatted string (excluding the NUL terminator) is stored into len. That
54+
// length is (size - 1) if the output was truncated, because when using
55+
// _vsnprintf() the would-be length is unknown.
56+
bool vsnprintfTrunc(char *buf, size_t size, int *len, const char *fmt, va_list args)
57+
{
58+
// This assertion guarantees that (size - 1) will not overflow in the
59+
// truncation case below, and that there is actually a location in which to
60+
// store the terminating NUL. The only reason to pass size=0 would be to
61+
// compute the formatted length, which should be done using
62+
// TR::[v]printfLen() instead.
63+
TR_ASSERT_FATAL(size > 0, "vsnprintfTrunc: no buffer space provided");
64+
65+
// This assertion guarantees that it's safe to truncate (size - 1) to int in
66+
// the string truncation case below.
67+
TR_ASSERT_FATAL(size - 1 <= (size_t)INT_MAX, "vsnprintfTrunc: buffer too large");
68+
69+
int n = stdlib_vsnprintf(buf, size, fmt, args);
70+
71+
// The formatted length does not include the NUL, so if (n >= size), the
72+
// result has been truncated. If we are using _vsnprintf(), it's possible to
73+
// get (n == size), which indicates that the entire formatted string has
74+
// been stored *without* a trailing NUL, but not (n > size). In the case
75+
// where standard vsnprintf() produces (n > size), _vsnprintf() returns a
76+
// negative value instead. So a negative result also indicates truncation.
77+
bool truncated = n < 0 || n >= size;
78+
if (truncated)
79+
{
80+
buf[size - 1] = '\0'; // Ensure NUL-termination in the _vsnprintf() case.
81+
*len = (int)(size - 1);
82+
}
83+
else
84+
{
85+
*len = n;
86+
}
87+
88+
return truncated;
89+
}
90+
91+
// See vsnprintfTrunc() above.
92+
bool snprintfTrunc(char *buf, size_t size, int *len, const char *fmt, ...)
93+
{
94+
va_list args;
95+
va_start(args, fmt);
96+
bool truncated = TR::vsnprintfTrunc(buf, size, len, fmt, args);
97+
va_end(args);
98+
return truncated;
99+
}
100+
101+
// Variant of vsnprintfTrunc() without the len pointer, for cases where the
102+
// caller is uninterested in the length.
103+
bool vsnprintfTrunc(char *buf, size_t size, const char *fmt, va_list args)
104+
{
105+
int dummy;
106+
return vsnprintfTrunc(buf, size, &dummy, fmt, args);
107+
}
108+
109+
// Variant of snprintfTrunc() without the len pointer, for cases where the
110+
// caller is uninterested in the length.
111+
bool snprintfTrunc(char *buf, size_t size, const char *fmt, ...)
112+
{
113+
va_list args;
114+
va_start(args, fmt);
115+
bool truncated = TR::vsnprintfTrunc(buf, size, fmt, args);
116+
va_end(args);
117+
return truncated;
118+
}
119+
120+
// Asserts that the formatted string was not truncated and returns its length
121+
// (excluding the NUL terminator).
122+
int vsnprintfNoTrunc(char *buf, size_t size, const char *fmt, va_list args)
123+
{
124+
int len;
125+
bool truncated = TR::vsnprintfTrunc(buf, size, &len, fmt, args);
126+
TR_ASSERT_FATAL(!truncated, "vsnprintfNoTrunc: truncation occurred");
127+
return len;
128+
}
129+
130+
// Asserts that the formatted string was not truncated and returns its length
131+
// (excluding the NUL terminator).
132+
int snprintfNoTrunc(char *buf, size_t size, const char *fmt, ...)
133+
{
134+
va_list args;
135+
va_start(args, fmt);
136+
int len = TR::vsnprintfNoTrunc(buf, size, fmt, args);
137+
va_end(args);
138+
return len;
139+
}
140+
141+
void TR::StringBuf::vappendf(const char *fmt, va_list args)
142+
{
143+
va_list argsCopy;
144+
145+
va_copy(argsCopy, args);
146+
int appendLen = TR::vprintfLen(fmt, argsCopy);
147+
va_copy_end(argsCopy);
148+
149+
TR_ASSERT_FATAL(appendLen >= 0, "error in format string");
150+
151+
size_t newLen = _len + appendLen;
152+
ensureCapacity(newLen);
153+
154+
TR_ASSERT_FATAL(appendLen + 1 <= _cap - _len, "insufficient buffer capacity");
155+
int realAppendLen = stdlib_vsnprintf(&_text[_len], appendLen + 1, fmt, args);
156+
TR_ASSERT_FATAL(realAppendLen == appendLen, "incorrect predicted snprintf length");
157+
TR_ASSERT_FATAL(_text[newLen] == '\0', "missing NUL terminator");
158+
159+
_len = newLen;
160+
}
161+
162+
void TR::StringBuf::appendf(const char *fmt, ...)
163+
{
164+
va_list args;
165+
va_start(args, fmt);
166+
vappendf(fmt, args);
167+
va_end(args);
168+
}
169+
170+
void TR::StringBuf::ensureCapacity(size_t newLen)
171+
{
172+
// Note that the capacity must always be strictly greater than the length so
173+
// that there is room for the NUL terminator.
174+
if (newLen < _cap)
175+
return; // nothing to do
176+
177+
size_t newCap = newLen + 1; // + 1 for NUL terminator
178+
179+
// Grow the buffer geometrically each time it is reallocated to ensure that
180+
// appends are amortized O(1) per character
181+
if (newCap < 2 * _cap)
182+
newCap = 2 * _cap;
183+
184+
char *newText = (char*)_region.allocate(newCap);
185+
memcpy(newText, _text, _len + 1); // + 1 for NUL terminator
186+
_text = newText;
187+
_cap = newCap;
188+
}
189+
190+
}

compiler/infra/String.hpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2021, 2021 IBM Corp. and others
3+
*
4+
* This program and the accompanying materials are made available under
5+
* the terms of the Eclipse Public License 2.0 which accompanies this
6+
* distribution and is available at http://eclipse.org/legal/epl-2.0
7+
* or the Apache License, Version 2.0 which accompanies this distribution
8+
* and is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
*
10+
* This Source Code may also be made available under the following Secondary
11+
* Licenses when the conditions for such availability set forth in the
12+
* Eclipse Public License, v. 2.0 are satisfied: GNU General Public License,
13+
* version 2 with the GNU Classpath Exception [1] and GNU General Public
14+
* License, version 2 with the OpenJDK Assembly Exception [2].
15+
*
16+
* [1] https://www.gnu.org/software/classpath/license.html
17+
* [2] http://openjdk.java.net/legal/assembly-exception.html
18+
*
19+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
20+
*******************************************************************************/
21+
22+
#ifndef TR_STRING_INCL
23+
#define TR_STRING_INCL
24+
25+
#include <stdarg.h>
26+
#include <stddef.h>
27+
#include "env/defines.h"
28+
#include "env/TRMemory.hpp"
29+
30+
#if HOST_COMPILER == COMPILER_GCC || HOST_COMPILER == COMPILER_CLANG
31+
#define TR_PRINTF_FORMAT_ATTR(fmtIndex, argsIndex) \
32+
__attribute__((format(printf, (fmtIndex), (argsIndex))))
33+
#else
34+
#define TR_PRINTF_FORMAT_ATTR(fmtIndex, argsIndex)
35+
#endif
36+
37+
namespace TR {
38+
39+
int vprintfLen(const char *fmt, va_list args);
40+
41+
int printfLen(const char *fmt, ...) TR_PRINTF_FORMAT_ATTR(1, 2);
42+
43+
bool vsnprintfTrunc(char *buf, size_t size, int *len, const char *fmt, va_list args);
44+
45+
bool snprintfTrunc(char *buf, size_t size, int *len, const char *fmt, ...)
46+
TR_PRINTF_FORMAT_ATTR(4, 5);
47+
48+
bool vsnprintfTrunc(char *buf, size_t size, const char *fmt, va_list args);
49+
50+
bool snprintfTrunc(char *buf, size_t size, const char *fmt, ...)
51+
TR_PRINTF_FORMAT_ATTR(3, 4);
52+
53+
int vsnprintfNoTrunc(char *buf, size_t size, const char *fmt, va_list args);
54+
55+
int snprintfNoTrunc(char *buf, size_t size, const char *fmt, ...)
56+
TR_PRINTF_FORMAT_ATTR(3, 4);
57+
58+
class StringBuf
59+
{
60+
TR::Region &_region;
61+
size_t _cap;
62+
size_t _len;
63+
char *_text;
64+
65+
// can't use a delegating constructor
66+
void initEmptyBuffer()
67+
{
68+
TR_ASSERT_FATAL(_cap > 0, "StringBuf: no buffer space");
69+
TR_ASSERT_FATAL(_text != NULL, "StringBuf: buffer is null");
70+
_text[0] = '\0';
71+
}
72+
73+
public:
74+
75+
// region, initialBuffer must both outlive this StringBuffer
76+
StringBuf(TR::Region &region, char *initialBuffer, size_t capacity)
77+
: _region(region), _cap(capacity), _len(0), _text(initialBuffer)
78+
{
79+
initEmptyBuffer();
80+
}
81+
82+
// region must outlive this StringBuffer
83+
StringBuf(TR::Region &region, size_t capacity = 32)
84+
: _region(region), _cap(capacity), _len(0), _text((char*)region.allocate(capacity))
85+
{
86+
initEmptyBuffer();
87+
}
88+
89+
size_t len() const { return _len; }
90+
const char *text() const { return _text; }
91+
92+
void clear()
93+
{
94+
_len = 0;
95+
_text[0] = '\0';
96+
}
97+
98+
void vappendf(const char *fmt, va_list args);
99+
100+
void appendf(const char *fmt, ...) TR_PRINTF_FORMAT_ATTR(2, 3);
101+
102+
private:
103+
104+
// Non-copyable. These are undefined and will cause a link error if anything
105+
// attempts to use them accidentally.
106+
StringBuf(const StringBuf &);
107+
StringBuf &operator=(const StringBuf &);
108+
109+
void ensureCapacity(size_t newLen);
110+
};
111+
112+
}
113+
114+
#endif

0 commit comments

Comments
 (0)