Finding orphan code in smaug

Posted by Robert Powell on Tue 19 Feb 2008 02:14 AM — 7 posts, 32,004 views.

Australia #0
I am again trying to Jenny Craig my code, and was wondering if there is there an easy way to find orphaned code in smaug, things like declares and unused structures, non called procedures and the like.

I have found a good deal of stuff that was left behind when some things were removed or changed, but im pretty sure there is a lot of others, which are harder to find without reading every C.file and checking what is used.
Australia Forum Administrator #1
I don't know of any personally, however a Google of "C cross-reference" showed this page, amongst others:

http://www.billauer.co.il/cdepend.html

He seems to have written a utility to solve that problem.
USA #2
You could do some clever tricks with regular expressions to find if functions are used; first build a list of function names and then see if/where those names appear elsewhere.

I think that there are programs that look for "lint" that also do this kind of stuff. See: http://en.wikipedia.org/wiki/Lint_programming_tool
Australia #3
Thanks David and Nick, i have downloaded splint and have started using it as it does not require that i compile differently, i have run 1 pass with it and it has caught a lot of little things already, that will help to clean things up.
USA #4
There's also a compiler flag in gcc, something like -Wunreachable-code, that will flag functions and statements the compiler thinks can't be reached. I've used it before with mixed results. There's also -Wunused-argument or something that will point out function arguments that aren't used in the functions, but with Smaug that generates a lot of scatter because of the DO_FUN format.

Splint is great for a lot more than just finding unused code. It points out all kinds of little nits that tend to escape the compiler itself.
Australia #5
Thanks Samson, i will add those compile flags latter and see what results i get from them and if i can find any other fluff thats not used.
Australia Forum Administrator #6
Here is one way of tackling the issue. As David said, a few regexps might find a bit of orphan code. I modified some code I had written for another SMAUG fixup project, namely at:

http://www.gammon.com.au/forum/?id=7139&page=2


The code below, which I ran in the MUSHclient immediate window, scans the Smaugfuss source .c files twice. The first time it attempts to find function bodies (and hence the names of the functions). The second time it sees if there are more than one reference to them by a simple find. Of course there will be one mention, which is the function declaration itself.

Here is the code, you probably need to change the path to the source:


-- pattern to detect a function declaration
pat = "[%a_]+%s+%*?%s*([%a%d_]+)%s*%b().-(%b{})"

olddir = "C:\\cygwin\\home\\Owner\\smaugfuss\\src\\"

funcs = {}

function process_function (name, body)
-- Note (" +++ function: " .. name)
  funcs [name] = 0
end -- process_function

function build_functions (name)
  local f, s

  print ("Looking for functions in: " .. name)
  f = assert (io.open (olddir .. name, "rb"))  -- open input
  s = f:read ("*a")  -- read all of it
  f:close ()  -- close it

  -- fix up imc strange function defs

  s = string.gsub (s, "PFUN%( ([%a%d_]+) %)",
                   "void %1( IMC_PACKET *q, char *packet )")
  s = string.gsub (s, "IMC_CMD%( ([%a%d_]+) %)",
                   "void %1( CHAR_DATA *ch, char *argument )")

  s = string.gsub (s, "'}'", "")  -- confuses function finding

  s = string.gsub (s, "/%*.-%*/", " ") -- remove comments

  s = string.gsub (s, "//.-\r?\n", "\n") -- remove comments

  string.gsub (s, pat, process_function)
 
end -- build_functions 

local t = assert (utils.readdir (olddir .. "*.c"))

for k in pairs (t) do
  build_functions (k)
end -- for

function count_it (name)
  if funcs [name] then
    funcs [name] = funcs [name] + 1
  end -- if
end -- count_it 

function check_xrefs (name)
  local f, s

  print ("Cross reference check in: " .. name)
  f = assert (io.open (olddir .. name, "rb"))  -- open input
  s = f:read ("*a")  -- read all of it
  f:close ()  -- close it

  string.gsub (s, "[%a%d_]+", count_it) 
end -- build_functions

for k in pairs (t) do
  check_xrefs (k)
end -- for

require "pairsbykeys"

for k, v in pairsByKeys (funcs) do
  if v <= 1 then
    if not string.match (k, "^do_") and
       not string.match (k, "^spell_") then
      print (k)
    end -- not likely to be spell or command
  end -- not referenced
end -- for



Running that gave me these results:


CONTENTS_NUM_ITEM
VAMP_AC
can_learn_lang
can_oedit
ch_slookup
chance_attrib
clean_resets
color_align
count_lines
economize_mobgold
ext_clear_bits
ext_has_bits
ext_is_empty
ext_remove_bits
ext_same_bits
ext_set_bits
ext_toggle_bits
find_skill
find_tongue
find_weapon
fwrite_bitvector
genrand_int31
genrand_real1
genrand_real3
get_exit_number
get_pcflag
get_wearloc
getcolor
imc_save_pfile
imc_savecolor
in_hash_table
in_soft_range
is_hunting
is_name_prefix
level_exp
make_fire
oprog_script_trigger
paint
personal_lookup
read_char_mobile
refresh_page
rprog_script_trigger
send_obj_page_to_char
smush_tilde
toggle_bexit_flag
web_imc_list
write_char_mobile


I'm not sure which are genuinely orphan functions, but that gives you a starting point for investigating.