// AnalyzeFiles processes and analyzes a bugreport package. func (pd *ParsedData) AnalyzeFiles(files []UploadedFile, refCount int) error { hasBugreport := false for _, file := range files { contents := string(file.Contents) switch file.FileType { case "bugreport", "bugreport2": // Parse the bugreport. if err := pd.parseBugReport(file.FileName, contents); err != nil { return fmt.Errorf("error parsing bugreport: %v", err) } if file.FileType == "bugreport" { hasBugreport = true } // Write the bug report to a file in case we need it to process a kernel trace file. if len(pd.data) < numberOfFilesToCompare { tmpFile, err := writeTempFile(contents) if err != nil { return fmt.Errorf("could not write bugreport: %v", err) } pd.bugReport = tmpFile } case "kernel": if !kernel.IsTrace(file.Contents) { return fmt.Errorf("invalid kernel trace file: %v", file.FileName) } if pd.kernelTrace != "" { log.Printf("more than one kernel trace file found") continue } // Need bug report to process kernel trace file, so store the file for later processing. tmpFile, err := writeTempFile(contents) if err != nil { return fmt.Errorf("could not write kernel trace file: %v", err) } pd.kernelTrace = tmpFile case "powermonitor": // Parse the powermonitor file. if err := pd.parsePowermonitorFile(file.FileName, contents); err != nil { return fmt.Errorf("error parsing powermonitor file: %v", err) } default: // File does not have a supported file type. return fmt.Errorf("invalid file %s of type %s", file.FileName, file.FileType) } } if !hasBugreport { return errors.New("missing bugreport file") } return nil }
// HTTPAnalyzeHandler processes the bugreport package uploaded via an http request's multipart body. func HTTPAnalyzeHandler(w http.ResponseWriter, r *http.Request) { // Do not accept files that are greater than 50 MBs if r.ContentLength > maxFileSize { closeConnection(w, "File too large (>50MB).") return } r.Body = http.MaxBytesReader(w, r.Body, maxFileSize) log.Printf("Trace starting reading uploaded file. %d bytes", r.ContentLength) defer log.Printf("Trace ended analyzing file.") var refCount int //get the multipart reader for the request. reader, err := r.MultipartReader() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var fs []UploadedFile //copy each part to destination. for { part, err := reader.NextPart() if err == io.EOF { break } // If part.FileName() is empty, skip this iteration. if part.FileName() == "" { continue } b, err := ioutil.ReadAll(part) if err != nil { http.Error(w, "Failed to read file. Please try again.", http.StatusInternalServerError) return } if len(b) == 0 { continue } files, err := bugreportutils.Contents(part.FileName(), b) if err != nil { http.Error(w, fmt.Sprintf("failed to read file contents: %v", err), http.StatusInternalServerError) return } var contents []byte valid := false fname := "" contentLoop: for n, f := range files { switch part.FormName() { case "bugreport", "bugreport2": if bugreportutils.IsBugReport(f) { // TODO: handle the case of additional kernel and powermonitor files within a single uploaded file valid = true contents = f fname = n break contentLoop } case "kernel": if kernel.IsTrace(f) { valid = true contents = f fname = n break contentLoop } case "powermonitor": if powermonitor.IsValid(f) { valid = true contents = f fname = n break contentLoop } default: valid = true contents = f fname = n break contentLoop } } if !valid { http.Error(w, fmt.Sprintf("%s does not contain a valid %s file", part.FileName(), part.FormName()), http.StatusInternalServerError) return } fs = append(fs, UploadedFile{part.FormName(), fname, contents}) } AnalyzeAndResponse(w, fs, refCount) }