File: memory_leaks.py

package info (click to toggle)
darktable 5.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 62,864 kB
  • sloc: ansic: 361,898; cpp: 102,446; xml: 19,813; lisp: 14,539; sh: 3,771; javascript: 3,264; perl: 1,925; python: 1,485; ruby: 975; makefile: 543; asm: 46; sql: 38; awk: 21
file content (108 lines) | stat: -rw-r--r-- 3,701 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#
#   This file is part of darktable,
#   Copyright (C) 2022 darktable developers.
#
#   darktable is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   darktable is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with darktable.  If not, see <http://www.gnu.org/licenses/>.
#

# Read each IOP file, parse it for buffer allocs, and check if we have a corresponding free

import os
import re
directory = "../src/iop/"

alloc_regex = r"([a-zA-Z0-9_\-\>\.\[\]]+) = (dt_|c|m|dt_opencl_)alloc.*\(.+\)"


def find_call_and_line(elem, file, directory):
  line_number = 0
  f = open(os.path.join(directory, file), "r")

  # find the line(s) in file
  for line in f:
    line_number += 1
    if elem in line:
      print("\t\t line", line_number, ":", elem.strip())

  f.close()

for file in sorted(os.listdir(directory)):
  if file.endswith(".c") or file.endswith(".h"):
    f = open(os.path.join(directory, file), "r")
    content = f.read()

    # Find all allocs and extract the name of the pointer to the allocation
    matches = re.finditer(alloc_regex, content, re.MULTILINE)

    safe_allocs = 0
    faulty_allocs = 0
    suspicious_allocs = 0

    print("%s" % file)

    for matchNum, match in enumerate(matches):
      # Get the name of the pointer to the allocation
      variable_name = match.group(1)
      alloc_type = match.group(2)

      # Look how many times the variable is allocated
      variable_alloc_regex = " %s = %salloc.*\(.+\)" % (variable_name, alloc_type)
      matches2 = re.findall(variable_alloc_regex, content, re.MULTILINE)
      allocs = len(matches2)
      matches2 = set(matches2)

      # Look how many times the variable is freed
      variable_free_regex = r""
      buffer_type = ""
      if(alloc_type == "dt_opencl_"):
        variable_free_regex  = ".*release_mem_object.*\(%s\)" % variable_name
        buffer_type = "OpenCL"
      else:
        variable_free_regex  = " \S*free.*\(%s\)" % variable_name
        buffer_type = "C"
      matches3 = re.findall(variable_free_regex, content, re.MULTILINE)
      frees = len(matches3)
      matches3 = set(matches3)

      # Note that for OpenCL, we may have more than one free for each alloc because of the error go-to
      if(frees < allocs and frees == 0):
        print("\tERROR: %s buffer `%s` is allocated %i time(s) but never freed" % (buffer_type, variable_name, allocs))
        for elem in matches2:
          find_call_and_line(elem, file, directory)
        for elem in matches3:
          find_call_and_line(elem, file, directory)

        faulty_allocs += 1

      elif(frees < allocs and frees > 0):
        print("\tWARNING: %s buffer `%s` is allocated %i time(s) but freed %i time(s)" % (buffer_type, variable_name, allocs, frees))
        for elem in matches2:
          find_call_and_line(elem, file, directory)
        for elem in matches3:
          find_call_and_line(elem, file, directory)

        suspicious_allocs += 1

      else:
        safe_allocs += 1

    msg_type = "INFO"
    if(suspicious_allocs > 0):
      msg_type = "WARNING"
    if(faulty_allocs > 0):
      msg_type = "ERROR"

    print("\t%s: %i safe alloc(s) detected over %i\n" % (msg_type, safe_allocs, safe_allocs + faulty_allocs + suspicious_allocs))

    f.close()