简体   繁体   中英

Segmentation fault (core dumped) over a 100 round loop bash script

I know there are a lot of questions on that famous "Segmentation fault core dump", but for what I can see, C/C++ is involved in most of them. But my issue is specific to a bash script, so I am trying my luck here.

I am running a simulator script (400+ lines so I cannot show you the code, can I?) which sets and calculates a fight between two characters using dices and a profile (like you would do in a Dungeons and Dragons tabletop RPG). I have a reinitialisation function to reset the fight from scratch so that it can start a new one when there is a winner. At the end of the X rounds, some values from it are displayed (% hit, %block %hp per round, etc).

If I set 1, 50 or 100 rounds (100 fights), it's okay, it runs perfectly. But over 130~ fights, all of sudden, it displays without any lag or other complications the "Segmentation fault (core dumped)" message.

I have a general idea of what it is, but I cannot explain why it happens, I cannot resolve it, and I do not know what to do, or what to look for.

Some notes I can say after browsing a lot of topics:

There is no import, no based command system, no sed, awk, no array, no "complicated" command. I am just playing with variables (integer). No string. The most "complexe" command (to get a random number) is probably

(echo $((1 + RANDOM % 20))) 

All my conditions are like this

if [[ "$Skill_Block2" == "Yes" ]];
then

With double bracket and variable between double quote (forgetting double quoted variable inside conditions could lead to problem, I heard).

There is no && or || or -a or -o (I also read that using direct "if" statement would be better)

The whole script is built around functions (easier to modify / implement). A function to calculate the damage is called by a function which check if a character can dodge the successful attack, which has been landed by another function allowing the success or the failure of that attack. Etc. I don't know if it is a good way of developping, but it "worked" so far.

I have accents and French characters, but they seem to be well managed by my OS version (Ubuntu).

I echo pretty much every single resolution so I can track mistakes. Perhaps displaying so much text is eating my virtual memory? But I would never expect that on a Linux, to be honest.

I don't think I have infinite loops since I can run it 50++ times without any problem in the exact same order.

To display the stats, I am using a dirty way (I think):

touch statistique.txt
    echo "#|Player 1|Player 2" > statistique.txt
    echo "ATT OK|$Number_Touch_OK1|$Number_Touch_OK1" >> statistique.txt
    echo "ATT Failed|$Number_Hit_Failed1|$Number_Hit_Failed2" >> statistique.txt
    echo "DEF Tried |$Number_Dodge_Tried1|$Number_Dodge_Tried2" >> statistique.txt
    [...]
    echo "Victory Number|$Victory1|$Victory2" >> statistique.txt
    echo " "
    column statistique.txt -t -s "|"

I tought about EOF, but I was not sure the variables would be interpreted. But at least I have a nicely formated text.

My Ubuntu is run on my Windows. Might be the problem?


So here I am. I feel confused, and I am not very enthusiastic about posting this message as a wall of text without any code because it's too long (but if someone is brave enough, I can share the code, no problem). I have seen too few message about memory leak on bash, so... I cannot imagine a Linux OS running out of memory If you have any idea, advice, software (I tried Valgrind, but again, I am not sure it works with a bash script), please let me know.

EDIT: here's the file (solveur.sh): https://github.com/IlliciteS/script

As pointed out in the comments, your program recurses endlessly. As each level of recursion consumes more memory, bash runs into the segmentation fault. Here's a minimal script to reproduce the problem (wrapped inside bash -c so that your interactive shell doesn't crash):

$ bash -c 'f() { f; }; f'
Segmentation fault (core dumped)

You can prevent the segfault by limiting the maximum recursion level using bash's FUNCNEST variable. However, this will only abort a bit more gracefully. To solve the actual problem you have to rewrite your script.

To identify the problem, You can look at the call graph of your script:

调用图,使用 <code>{ fNames=$(grep -Po '^\w+(?=\(\))' solveur.sh); echo "有向图调用图 {"; tr \\n \\r < 求解器.sh | grep -Po '\r\K\w+\(\).*?\r\}' |同时读取-r f;做; f=${f//$'\r'/$'\n'}; fName=$(echo "$f" | grep -Pom1 '\w+');回声“$fName”;回声 "$f" |尾-n+2 | grep -Fwof <(echo "$fNames") | sed "s/^/$fName -> /";完毕;回声“}”; } > callgraph.dot;</code> 并且可能不是 100% 准确的

Every cycle inside this graph could max out the call stack. A bit of recursion is ok. But for your task, loops seem to be more natural anyway. I would start by using a loop to start new fights. Delete Relance_Match() and change your main "method" to

for ((i=0; i<Nombre_Match_Total; i++)); do
  Who_start_fight
  echo "..."
  Reinitialisation_Carac_New_Match
done
echo "..."
Statistique

Further Improvments Unrelated To Recursion

You can drastically simplify your script with arrays. Right now, each player has its own set of variables and code fragments, eg

# for player one
Victoire1=0
Touch1() {
  # lots of code using <insertThingHere>1, e.g.
  ((Victoire1++))
}

# for player two
Victoire2=0
Touch2() {
  # the same code as in Touch1, but with <insertThingHere>2 instead, e.g.
  ((Victoire2++))
}

With arrays this could be simplified to

Victoire=(0 0)
Touch() {
  player=$1
  # lots of code using <insertThingHere>[player], e.g.
  ((Victoire[player]++));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM