Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 84 additions & 20 deletions source/dcautil/dca.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,28 +386,31 @@ int processTopPattern(char* profileName, Vector* topMarkerList, int profileExec
* @return Returns the status of the operation.
* @retval Returns -1 on failure, appropriate errorcode otherwise.
*/
static int getLogSeekValue(hash_map_t *logSeekMap, const char *name, long *seek_value)
static int getLogSeekValue(hash_map_t *logSeekMap, const char *name, long *seek_value, ino_t *stored_inode)
{

T2Debug("%s ++in for file %s \n", __FUNCTION__, name);
int rc = 0;
if (logSeekMap)
{
long *data = (long*) hash_map_get(logSeekMap, name) ;
LogSeekInfo *data = (LogSeekInfo*) hash_map_get(logSeekMap, name) ;
if (data)
{
*seek_value = *data ;
*seek_value = data->seekValue ;
*stored_inode = data->inode ;
}
else
{
T2Debug("data is null .. Setting seek value to 0 from getLogSeekValue \n");
*seek_value = 0 ;
*stored_inode = 0 ;
}
}
else
{
T2Debug("logSeekMap is null .. Setting seek value to 0 \n");
*seek_value = 0 ;
*stored_inode = 0 ;
}
T2Debug("seekvalue for file %s is %ld\n", name, *seek_value);
T2Debug("%s --out \n", __FUNCTION__);
Expand All @@ -422,20 +425,21 @@ static int getLogSeekValue(hash_map_t *logSeekMap, const char *name, long *seek_
* @return Returns the status of the operation.
* @retval Returns -1 on failure, appropriate errorcode otherwise.
*/
static T2ERROR updateLogSeek(hash_map_t *logSeekMap, const char* logFileName, const long logfileSize)
static T2ERROR updateLogSeek(hash_map_t *logSeekMap, const char* logFileName, const long logfileSize, const ino_t inode)
{
T2Debug("%s ++in\n", __FUNCTION__);
if(logSeekMap == NULL || logFileName == NULL)
{
T2Error("Invalid or NULL arguments\n");
return T2ERROR_FAILURE;
}
T2Debug("Adding seekvalue of %ld for %s to logSeekMap \n", logfileSize, logFileName);
long* val = (long *) malloc(sizeof(long));
T2Debug("Adding seekvalue of %ld (inode: %lu) for %s to logSeekMap \n", logfileSize, (unsigned long)inode, logFileName);
LogSeekInfo* val = (LogSeekInfo *) malloc(sizeof(LogSeekInfo));
if(NULL != val)
{
memset(val, 0, sizeof(long));
*val = logfileSize;
memset(val, 0, sizeof(LogSeekInfo));
val->seekValue = logfileSize;
val->inode = inode;
hash_map_put(logSeekMap, strdup(logFileName), (void *)val, free);
}
else
Expand Down Expand Up @@ -867,10 +871,11 @@ static int processPatternWithOptimizedFunction(GrepMarker* marker, FileDescripto
return 0;
}

static int getLogFileDescriptor(GrepSeekProfile* gsProfile, const char* logPath, const char* logFile, int old_fd, off_t* out_seek_value)
static int getLogFileDescriptor(GrepSeekProfile* gsProfile, const char* logPath, const char* logFile, int old_fd, off_t* out_seek_value, bool* out_inode_changed, ino_t* out_stored_inode)
{
long seek_value_from_map = 0;
getLogSeekValue(gsProfile->logFileSeekMap, logFile, &seek_value_from_map);
ino_t stored_inode = 0;
getLogSeekValue(gsProfile->logFileSeekMap, logFile, &seek_value_from_map, &stored_inode);
if (old_fd != -1)
{
close(old_fd);
Expand Down Expand Up @@ -912,14 +917,25 @@ static int getLogFileDescriptor(GrepSeekProfile* gsProfile, const char* logPath,
}

// Check if the file size matches the seek value from the map
if (sb.st_size == seek_value_from_map)
if (sb.st_size == seek_value_from_map && sb.st_ino == stored_inode)
{
T2Debug("The logfile size matches the seek value (%ld) for %s\n", seek_value_from_map, logFile);
close(fd);
return -1; // Consistent error return value
}
updateLogSeek(gsProfile->logFileSeekMap, logFile, sb.st_size);

// Detect inode change: if inode differs from stored value, the file was rotated
// This covers the corner case where new file grows past seek_value after rotation
*out_inode_changed = (stored_inode != 0 && sb.st_ino != stored_inode);
if (*out_inode_changed)
{
T2Info("Inode changed for %s (stored: %lu, current: %lu) - log rotation detected\n",
logFile, (unsigned long)stored_inode, (unsigned long)sb.st_ino);
}

updateLogSeek(gsProfile->logFileSeekMap, logFile, sb.st_size, sb.st_ino);
*out_seek_value = seek_value_from_map;
*out_stored_inode = stored_inode;
return fd;
}

Expand Down Expand Up @@ -1000,7 +1016,7 @@ static void freeFileDescriptor(FileDescriptor* fileDescriptor)
}
}

static FileDescriptor* getFileDeltaInMemMapAndSearch(const int fd, const off_t seek_value, const char* logPath, const char* logFile, bool check_rotated )
static FileDescriptor* getFileDeltaInMemMapAndSearch(const int fd, const off_t seek_value, const char* logPath, const char* logFile, bool check_rotated, bool inode_changed, ino_t stored_inode)
{
char *addrcf = NULL;
char *addrrf = NULL;
Expand Down Expand Up @@ -1065,7 +1081,9 @@ static FileDescriptor* getFileDeltaInMemMapAndSearch(const int fd, const off_t s
return NULL;
}

if(seek_value > sb.st_size || check_rotated == true)
// Inode change means rotation happened even if current file size > seek_value
// In this case, read the rotated file from seek_value and the entire current file
if(seek_value > sb.st_size || check_rotated == true || inode_changed == true)
{
int rd = getRotatedLogFileDescriptor(logPath, logFile);
if (rd != -1 && fstat(rd, &rb) == 0 && rb.st_size > 0)
Expand Down Expand Up @@ -1113,31 +1131,75 @@ static FileDescriptor* getFileDeltaInMemMapAndSearch(const int fd, const off_t s
return NULL;
}

if(rb.st_size > seek_value)
// Determine if the rotated file is the ORIGINAL file we previously read
// (its inode matches our stored inode) or an intermediate file from
// multiple rotations (different inode — we've never seen this content).
bool rotated_is_original = (stored_inode != 0 && rb.st_ino == stored_inode);

if(rotated_is_original && rb.st_size > seek_value)
{
// Rotated file IS the original — apply seek_value to skip already-read data
// Read unread tail of rotated + entire new current file
rotated_fsize = rb.st_size - seek_value;
main_fsize = sb.st_size;
bytes_ignored_rotated = bytes_ignored;
addrcf = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, tmp_fd, 0);
addrrf = mmap(NULL, rb.st_size, PROT_READ, MAP_PRIVATE, tmp_rd, offset_in_page_size_multiple);
}
else if(rotated_is_original && rb.st_size <= seek_value)
{
// Rotated file is original but we already read past its end — skip it
// Read entire new current file
rotated_fsize = 0;
main_fsize = sb.st_size;
bytes_ignored_main = 0;
addrcf = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, tmp_fd, 0);
addrrf = NULL;
close(tmp_rd);
tmp_rd = -1;
}
else if(inode_changed)
{
// Rotated file is NOT the original (multi-rotation: intermediate file)
// We have never seen this content — read BOTH files entirely from 0
T2Info("Multi-rotation detected: rotated file inode %lu != stored %lu, reading both from 0\n",
(unsigned long)rb.st_ino, (unsigned long)stored_inode);
rotated_fsize = rb.st_size;
main_fsize = sb.st_size;
bytes_ignored_main = 0;
addrcf = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, tmp_fd, 0);
addrrf = mmap(NULL, rb.st_size, PROT_READ, MAP_PRIVATE, tmp_rd, 0);
}
else
{
// No inode change (seek_value > sb.st_size or check_rotated path)
// Legacy behavior: read entire rotated + current from seek
rotated_fsize = rb.st_size;
main_fsize = sb.st_size - seek_value;
bytes_ignored_main = bytes_ignored;
addrcf = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, tmp_fd, offset_in_page_size_multiple);
addrrf = mmap(NULL, rb.st_size, PROT_READ, MAP_PRIVATE, tmp_rd, 0);
}
Comment thread
yogeswaransky marked this conversation as resolved.
close(tmp_rd);
if(tmp_rd != -1)
{
close(tmp_rd);
}
close(rd);
rd = -1;
}
else
{
T2Debug("Error opening rotated file. Start search in current file\n");
T2Debug("File size rounded to nearest page size used for offset read: %jd bytes\n", (intmax_t)offset_in_page_size_multiple);
if(seek_value < sb.st_size)
if(inode_changed)
{
// Inode changed but rotated file unavailable - read entire current file
T2Info("Rotated file unavailable after inode change, reading entire current file\n");
addrcf = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, tmp_fd, 0);
bytes_ignored_main = 0;
main_fsize = sb.st_size;
}
else if(seek_value < sb.st_size)
{
addrcf = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, tmp_fd, offset_in_page_size_multiple);
bytes_ignored_main = bytes_ignored;
Expand Down Expand Up @@ -1316,15 +1378,17 @@ static int parseMarkerListOptimized(GrepSeekProfile *gsProfile, Vector * ip_vMar

// Get a valid file descriptor for the current log file
off_t seek_value = 0;
fd = getLogFileDescriptor(gsProfile, logPath, log_file_for_this_iteration, fd, &seek_value);
bool inode_changed = false;
ino_t prev_stored_inode = 0;
fd = getLogFileDescriptor(gsProfile, logPath, log_file_for_this_iteration, fd, &seek_value, &inode_changed, &prev_stored_inode);
prevfile = updateFilename(prevfile, log_file_for_this_iteration);
if (fd == -1)
{
T2Debug("Error in creating file descriptor for file %s\n", log_file_for_this_iteration);
continue;
}

fileDescriptor = getFileDeltaInMemMapAndSearch(fd, seek_value, logPath, log_file_for_this_iteration, check_rotated_logs);
fileDescriptor = getFileDeltaInMemMapAndSearch(fd, seek_value, logPath, log_file_for_this_iteration, check_rotated_logs, inode_changed, prev_stored_inode);

if (fd != -1)
{
Expand Down Expand Up @@ -1412,7 +1476,7 @@ extractUnixTimestampFunc extractUnixTimestampFuncCallback(void)
return extractUnixTimestamp;
}

typedef T2ERROR (*updateLogSeekFunc)(hash_map_t *, const char *, const long);
typedef T2ERROR (*updateLogSeekFunc)(hash_map_t *, const char *, const long, const ino_t);
updateLogSeekFunc updateLogSeekFuncCallback(void)
{
return updateLogSeek;
Expand Down
62 changes: 48 additions & 14 deletions source/dcautil/dcautil.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,30 @@ T2ERROR saveSeekConfigtoFile(char* profileName, GrepSeekProfile *ProfileSeekMap)
cJSON *valArray = cJSON_CreateArray();
for (unsigned int i = 0; i < count ; i++)
{
char *logFileName = NULL;
long *seekvalue = NULL;
logFileName = hash_map_lookupKey(logfileMap, i);
seekvalue = hash_map_lookup(logfileMap, i);
char *logFileName = hash_map_lookupKey(logfileMap, i);
LogSeekInfo *seekInfo = (LogSeekInfo *)hash_map_lookup(logfileMap, i);
if (!logFileName || !seekInfo)
{
T2Warning("Skipping entry %u: NULL key or value in logFileSeekMap\n", i);
continue;
}
cJSON *logFileObj = cJSON_CreateObject();
cJSON_AddNumberToObject(logFileObj, logFileName, (double)*seekvalue);
cJSON_AddItemToArray(valArray, logFileObj);
if (!logFileObj)
{
T2Error("Failed to allocate cJSON object for seek entry\n");
continue;
}
cJSON_AddNumberToObject(logFileObj, "seekValue", (double)seekInfo->seekValue);
cJSON_AddNumberToObject(logFileObj, "inode", (double)seekInfo->inode);
Comment thread
yogeswaransky marked this conversation as resolved.
cJSON *wrapper = cJSON_CreateObject();
Comment thread
yogeswaransky marked this conversation as resolved.
if (!wrapper)
{
T2Error("Failed to allocate cJSON wrapper object\n");
cJSON_Delete(logFileObj);
continue;
}
cJSON_AddItemToObject(wrapper, logFileName, logFileObj);
cJSON_AddItemToArray(valArray, wrapper);
Comment thread
yogeswaransky marked this conversation as resolved.
}
char *jsonReport = cJSON_PrintUnformatted(valArray);
if(T2ERROR_SUCCESS != saveConfigToFile(SEEKFOLDER, profileName, jsonReport))
Expand Down Expand Up @@ -170,15 +187,32 @@ T2ERROR loadSavedSeekConfig(char *profileName, GrepSeekProfile *ProfileSeekMap)

if (key != NULL)
{
// Check the value type and print it
if (cJSON_IsNumber(value))
// New format: {"logFileName": {"seekValue": N, "inode": M}}
if (cJSON_IsObject(value))
{
cJSON *seekVal = cJSON_GetObjectItem(value, "seekValue");
cJSON *inodeVal = cJSON_GetObjectItem(value, "inode");
if (cJSON_IsNumber(seekVal))
{
LogSeekInfo *info = (LogSeekInfo *)malloc(sizeof(LogSeekInfo));
if (info)
{
info->seekValue = (long)seekVal->valuedouble;
info->inode = (inodeVal && cJSON_IsNumber(inodeVal)) ? (ino_t)inodeVal->valuedouble : 0;
hash_map_put(ProfileSeekMap->logFileSeekMap, strdup(key), info, free);
}
}
Comment thread
yogeswaransky marked this conversation as resolved.
}
// Legacy format: {"logFileName": N}
else if (cJSON_IsNumber(value))
{
long *tempnum;
double val = value->valuedouble;
tempnum = (long *)malloc(sizeof(long));
*tempnum = (long)val;
hash_map_put(ProfileSeekMap->logFileSeekMap, strdup(key), tempnum, NULL);
//printf("Key: %s, Value: %ld\n", key, *tempnum);
LogSeekInfo *info = (LogSeekInfo *)malloc(sizeof(LogSeekInfo));
if (info)
{
info->seekValue = (long)value->valuedouble;
info->inode = 0; // No inode info in legacy format
hash_map_put(ProfileSeekMap->logFileSeekMap, strdup(key), info, free);
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions source/dcautil/legacyutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/types.h>
#include <cjson/cJSON.h>

#include "t2collection.h"
Expand Down Expand Up @@ -63,10 +64,17 @@
#endif

/*
* Used to store the log file seek position for each profile.
* Used to store the log file seek position and inode for each profile.
* The key is the profile name and
* value is a hash_map_t which contains the log file name as key and the seek position as value.
* value is a hash_map_t which contains the log file name as key and LogSeekInfo as value.
* The inode is used to detect log rotation even when the new file grows past the previous seek value.
*/
typedef struct _LogSeekInfo
{
long seekValue;
ino_t inode;
} LogSeekInfo;

typedef struct _GrepSeekProfile
{
hash_map_t *logFileSeekMap;
Expand Down
Loading
Loading