Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
abetlen committed Jan 2, 2025
0 parents commit d17296e
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Build and Test
on:
push:
branches: [ main ]
workflow_dispatch:
env:
MORPH_API_KEY: ${{ secrets.MORPH_API_KEY }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install morphcloud
run: |
pip install git+https://github.com/morph-labs/morph-python-sdk.git
- name: Start instance and get ID
run: |
echo "INSTANCE_ID=$(morphcloud instance start snapshot_do2mk83p)" >> $GITHUB_ENV
- name: Copy files to MorphVM
run: |
morphcloud instance copy -r . $INSTANCE_ID:.
- name: Install dependencies on MorphVM
run: |
morphcloud instance ssh $INSTANCE_ID -- apt update
morphcloud instance ssh $INSTANCE_ID -- apt install -y gcc gdb make tmux python3
- name: Build program on MorphVM
run: |
morphcloud instance ssh $INSTANCE_ID -- make
- name: Generate test input
run: |
morphcloud instance ssh $INSTANCE_ID -- 'python3 test_input.py > test_data.txt'
- name: Run test in tmux/gdb
id: gdb-run
continue-on-error: true
run: |
morphcloud instance ssh $INSTANCE_ID -- tmux new-session -d 'gdb -ex "handle SIGABRT stop" -ex "run test_data.txt" -ex "bt" ./text_analyzer'
sleep 5
if morphcloud instance ssh $INSTANCE_ID -- tmux list-sessions; then
SNAPSHOT_ID=$(morphcloud instance snapshot $INSTANCE_ID)
echo "SNAPSHOT_ID=${SNAPSHOT_ID}" >> $GITHUB_ENV
exit 1
fi
- name: Stopping MorphVM
if: always()
run: |
morphcloud instance stop $INSTANCE_ID
- name: Report failure
if: steps.gdb-run.outcome == 'failure'
run: |
echo "GDB encountered an error. To debug, run:"
echo "morphcloud instance ssh --rm \$(morphcloud instance start ${{ env.SNAPSHOT_ID }}) -- tmux attach"
exit 1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.envrc
22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
CC = gcc
CFLAGS = -g -Wall -Wextra -fsanitize=address -fno-omit-frame-pointer
LDFLAGS = -fsanitize=address

.PHONY: all clean run debug test

all: text_analyzer

text_analyzer: text_analyzer.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)

run: text_analyzer
./text_analyzer "The quick brown fox jumps over the lazy dog"

test: text_analyzer test_input.py
python3 test_input.py | ./text_analyzer

debug: text_analyzer
gdb ./text_analyzer

clean:
rm -f text_analyzer
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# github-actions-example
24 changes: 24 additions & 0 deletions test_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env python3

def generate_test_text():
# Create words of different lengths
words = [
"the", # length 3
"word", # length 4
"count", # length 5 - will trigger the bug
"system", # length 6
"analyzer" # length 8
]

# Generate enough words to force multiple resizes
result = []
for i in range(100):
result.extend(words)
# Add some variations to ensure unique words
result.append(f"word{i}")
result.append(f"count{i}") # More length-5 words to increase bug chance

return " ".join(result)

if __name__ == "__main__":
print(generate_test_text())
137 changes: 137 additions & 0 deletions text_analyzer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_WORDS 1000
#define MAX_WORD_LENGTH 100

typedef struct word_node {
char* word;
int count;
struct word_node* left;
struct word_node* right;
} WordNode;

WordNode* create_node(const char* word) {
WordNode* node = malloc(sizeof(WordNode));
if (!node) return NULL;

node->word = strdup(word);
node->count = 1;
node->left = node->right = NULL;
return node;
}

WordNode* find_or_insert(WordNode* root, const char* word) {
// NOTE: this word counting program contains a not-so-subtle memory error
// we are going to use gdb running inside of a morphvm to debug it
root->count++;

if (!root) {
return create_node(word);
}

int cmp = strcmp(word, root->word);

if (cmp == 0) {
root->count++;
return root;
}
if (cmp < 0) {
root->left = find_or_insert(root->left, word);
return root;
}
return find_or_insert(root->right, word);
}

void normalize_word(char* word) {
char* src = word;
char* dst = word;
while (*src) {
if (isalpha(*src)) {
*dst = tolower(*src);
dst++;
}
src++;
}
*dst = '\0';
}

void print_tree(WordNode* root) {
if (!root) return;

print_tree(root->left);
printf("%-20s: %d\n", root->word, root->count);
print_tree(root->right);
}

void free_tree(WordNode* root) {
if (!root) return;

free_tree(root->left);
free_tree(root->right);
free(root->word);
free(root);
}

int process_file(WordNode** root, FILE* fp) {
char word[MAX_WORD_LENGTH];
int c;
size_t pos = 0;

while ((c = fgetc(fp)) != EOF) {
if (isalpha(c)) {
if (pos < MAX_WORD_LENGTH - 1) {
word[pos++] = c;
}
} else if (pos > 0) {
word[pos] = '\0';
normalize_word(word);

// This will segfault when trying to use the dangling pointer
WordNode* found = find_or_insert(*root, word);
if (!*root) *root = found;

pos = 0;
}
}

if (pos > 0) {
word[pos] = '\0';
normalize_word(word);
WordNode* found = find_or_insert(*root, word);
if (!*root) *root = found;
}

return 1;
}

int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: %s <input_file>\n", argv[0]);
return 1;
}

FILE* fp = fopen(argv[1], "r");
if (!fp) {
printf("Error opening file: %s\n", argv[1]);
return 1;
}

WordNode* root = NULL;
if (!process_file(&root, fp)) {
printf("Error processing file\n");
free_tree(root);
fclose(fp);
return 1;
}

printf("\nWord Counts:\n");
printf("------------\n");
print_tree(root);

free_tree(root);
fclose(fp);
return 0;
}

0 comments on commit d17296e

Please sign in to comment.