-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d17296e
Showing
6 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.envrc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# github-actions-example |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |