Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit d20f35e

Browse files
author
Juanjo Alvarez
committed
Memleaks and other fixes
- Fixed several memleaks. All runaway memleaks seem to be fixed, there is still an initial fixed-size leak after the first call to filter(). - Removed redundant method in memtracker (and calls to it). - Other small improvements. Signed-off-by: Juanjo Alvarez <juanjo@sourced.tech>
1 parent 6419d41 commit d20f35e

5 files changed

Lines changed: 51 additions & 33 deletions

File tree

bblfsh/memtracker.cc

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,20 @@ void MemTracker::TrackItem(PyObject *o)
1616
}
1717
}
1818

19-
void MemTracker::TrackStr(PyObject *o)
20-
{
21-
if (inFilter_) {
22-
filterStrAllocs_.push_back(o);
23-
} else {
24-
iterStrAllocs_[currentIter_].push_back(o);
25-
}
26-
}
27-
2819
void MemTracker::DisposeMem()
2920
{
3021
if (inFilter_) {
31-
for (auto &i : filterStrAllocs_) {
32-
Py_XDECREF(i);
33-
i = nullptr;
34-
}
3522
for (auto &i : filterItemAllocs_) {
36-
Py_XDECREF(i);
37-
i = nullptr;
23+
Py_CLEAR(i);
3824
}
25+
filterItemAllocs_.clear();
26+
filterItemAllocs_.shrink_to_fit();
3927
} else {
40-
for (auto &i : iterStrAllocs_[currentIter_]) {
41-
Py_XDECREF(i);
42-
i = nullptr;
43-
}
4428
for (auto &i : iterItemAllocs_[currentIter_]) {
45-
Py_XDECREF(i);
46-
i = nullptr;
29+
Py_CLEAR(i);
4730
}
31+
iterItemAllocs_[currentIter_].clear();
32+
iterItemAllocs_.erase(currentIter_);
4833
ClearCurrentIterator();
4934
}
5035
}

bblfsh/memtracker.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ class MemTracker {
1111
bool inFilter_ = false;
1212

1313
std::unordered_map<UastIterator*, std::vector<PyObject*>> iterItemAllocs_;
14-
std::unordered_map<UastIterator*, std::vector<PyObject*>> iterStrAllocs_;
1514
std::vector<PyObject*> filterItemAllocs_;
16-
std::vector<PyObject*> filterStrAllocs_;
1715

1816
public:
1917
UastIterator *CurrentIterator();
@@ -23,6 +21,5 @@ class MemTracker {
2321
void EnterFilter();
2422
void ExitFilter();
2523
void TrackItem(PyObject *ref);
26-
void TrackStr(PyObject *ref);
2724
void DisposeMem();
2825
};

bblfsh/pyuast.cc

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
MemTracker memTracker;
1212

1313
// Used to store references to the Pyobjects instanced in String() and
14-
// ItemAt() methods. Those can't be DECREF'ed to 0 because libuast uses the
14+
// ItemAt() methods. Those can't be DECREF'ed to 0 because libuast uses them
1515
// so we pass ownership to these lists and free them at the end of filter()
1616

1717
static PyObject *Attribute(const void *node, const char *prop) {
@@ -29,7 +29,7 @@ static const char *String(const void *node, const char *prop) {
2929
PyObject *o = Attribute(node, prop);
3030
if (o != NULL) {
3131
retval = PyUnicode_AsUTF8(o);
32-
memTracker.TrackStr(o);
32+
memTracker.TrackItem(o);
3333
}
3434
return retval;
3535
}
@@ -39,6 +39,7 @@ static size_t Size(const void *node, const char *prop) {
3939
PyObject *o = Attribute(node, prop);
4040
if (o != NULL) {
4141
retval = PySequence_Size(o);
42+
Py_DECREF(o);
4243
}
4344

4445
return retval;
@@ -69,21 +70,37 @@ static size_t ChildrenSize(const void *node) {
6970

7071
static void *ChildAt(const void *node, int index) {
7172
PyObject *children = AttributeValue(node, "children");
72-
return children ? ItemAt(children, index) : NULL;
73+
void *retval = nullptr;
74+
if (children) {
75+
retval = ItemAt(children, index);
76+
Py_DECREF(children);
77+
}
78+
79+
return retval;
7380
}
7481

7582
static size_t RolesSize(const void *node) {
7683
return Size(node, "roles");
7784
}
7885

7986
static uint16_t RoleAt(const void *node, int index) {
87+
uint16_t retval = 0;
8088
PyObject *roles = AttributeValue(node, "roles");
81-
return roles ? (uint16_t)PyLong_AsUnsignedLong(ItemAt(roles, index)) : 0;
89+
if (roles) {
90+
retval = (uint16_t)PyLong_AsUnsignedLong(ItemAt(roles, index));
91+
Py_DECREF(roles);
92+
}
93+
return retval;
8294
}
8395

8496
static size_t PropertiesSize(const void *node) {
97+
size_t retval = 0;
8598
PyObject *properties = AttributeValue(node, "properties");
86-
return properties ? PyMapping_Size(properties) : 0;
99+
if (properties) {
100+
retval = PyMapping_Size(properties);
101+
Py_DECREF(properties);
102+
}
103+
return retval;
87104
}
88105

89106
static const char *PropertyKeyAt(const void *node, int index) {
@@ -94,6 +111,7 @@ static const char *PropertyKeyAt(const void *node, int index) {
94111

95112
const char *retval = NULL;
96113
PyObject *keys = PyMapping_Keys(properties);
114+
Py_DECREF(properties);
97115
if (keys != NULL) {
98116
retval = PyUnicode_AsUTF8(ItemAt(keys, index));
99117
Py_DECREF(keys);
@@ -113,6 +131,7 @@ static const char *PropertyValueAt(const void *node, int index) {
113131
retval = PyUnicode_AsUTF8(ItemAt(values, index));
114132
Py_DECREF(values);
115133
}
134+
Py_DECREF(properties);
116135
return retval;
117136
}
118137

@@ -123,7 +142,14 @@ static uint32_t PositionValue(const void* node, const char *prop, const char *fi
123142
}
124143

125144
PyObject *offset = AttributeValue(position, field);
126-
return offset ? (uint32_t)PyLong_AsUnsignedLong(offset) : 0;
145+
Py_DECREF(position);
146+
uint32_t retval = 0;
147+
148+
if (offset) {
149+
retval = (uint32_t)PyLong_AsUnsignedLong(offset);
150+
Py_DECREF(offset);
151+
}
152+
return retval;
127153
}
128154

129155
/////////////////////////////////////
@@ -337,6 +363,7 @@ static PyObject *PyFilter(PyObject *self, PyObject *args)
337363
cleanupFilter();
338364
return NULL;
339365
}
366+
340367
size_t len = NodesSize(nodes);
341368
PyObject *list = PyList_New(len);
342369

bblfsh/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def testManyFilters(self):
252252

253253
import resource
254254
before = resource.getrusage(resource.RUSAGE_SELF)
255-
for _ in range(100):
255+
for _ in range(500):
256256
filter(root, "//*[@roleIdentifier]")
257257
after = resource.getrusage(resource.RUSAGE_SELF)
258258

setup.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
from setuptools import setup, find_packages, Extension
66
from setuptools.command.build_ext import build_ext
77

8-
VERSION = "2.9.6"
8+
VERSION = "2.9.8"
99
LIBUAST_VERSION = "v1.9.1"
1010
SDK_VERSION = "v1.8.0"
1111
SDK_MAJOR = SDK_VERSION.split('.')[0]
1212
PYTHON = "python3"
1313

14+
# For debugging libuast-client interactions, set to True in production!
15+
GET_LIBUAST = True
16+
if not GET_LIBUAST:
17+
print('WARNING: not retrieving libuast, using local version')
18+
1419
os.environ["CC"] = "g++"
1520
os.environ["CXX"] = "g++"
1621
libraries = ['xml2']
@@ -69,6 +74,9 @@ def createInits():
6974

7075

7176
def getLibuast():
77+
if not GET_LIBUAST:
78+
return
79+
7280
runc("curl -SL https://github.com/bblfsh/libuast/releases/download/{LIBUAST_VERSION}/"
7381
"libuast-{LIBUAST_VERSION}.tar.gz | tar xz")
7482
runc("mv libuast-{LIBUAST_VERSION} libuast")
@@ -108,7 +116,8 @@ def clean():
108116
runc("rm -rf gopkg.in")
109117
runc("rm -rf bblfsh/github")
110118
runc("rm -rf bblfsh/gopkg")
111-
runc("rm -rf bblfsh/libuast")
119+
if GET_LIBUAST:
120+
runc("rm -rf bblfsh/libuast")
112121

113122

114123
def main():

0 commit comments

Comments
 (0)